I’ve decided to add search functionality to my website TallyMusic.net. The site tracks local and touring acts coming through Tallahassee, and I think it will be more useful for finding particular things by being able to search the site.
urls.py
import concerts.views as concert_views urlpatterns = [ ... url(r'^search/$', concert_views.search, name="search") ]
The search function will be searching thorugh my concerts
app, so that’s where I’ll be writing my view. I could have put it in my concerts
app’s urls.py
file, but it seemed right to me to put it in the project urls.py
file, since it will be used across the site. I’d be happy to entertain other theories on this.
concerts/views.py
def search(request): if request.method == 'GET': search_term = request.GET.get('search') try: headliners = Concert.objects.filter(headliner__search=search_term) notes = Concert.objects.filter(notes__search=search_term) support = Concert.objects.filter(support__search=search_term) venues = Concert.objects.filter(venue__name__search=search_term) prices = Concert.objects.filter(price__search=search_term) ages = Concert.objects.filter(age__search=search_term) results = headliners | notes | support | venues | prices | ages except Concert.DoesNotExist: results = None template = "concerts/search.html" context = {"results" : results, "search_term" : search_term} return render(request, template, context) else: return render(request, template, {})
Here we’re taking the user’s search term using request.GET.get('search')
, then searching several fields of the model for possible matches (seen in the try
clause). After that, we simply return
a render()
of the results.
concerts/search.html
{% extends 'base.html' %} {% block content %} <h1>Search for: {{ search_term }}</h1> <p>Found {{ results|length }} result{{ results|pluralize }}.</p> <div class="concerts"> {% if results %} {% for result in results %} <div class="concert"> <div class="date"> <span class="day">{{ result.date.day }}</span> <span class="month">{{ result.date|date:'F' }}</span> <span class="year">{{ result.date.year }}</span> </div> <div class="details"> <div class="bg-image"></div> <span class="headliner">{{ result.headliner }}</span> <span class="support">{{ result.support }}</span> <span class="price">{{ result.price }}</span> {% if result.notes %} <div class="notes open">Click for more information ↓</div> <div class="notes">{{ result.notes|linebreaksbr }}</div> <div class="notes">Click to hide ↑</div> {% endif %} <span class="concert_website"> <a href="{{ result.website }}" target="_blank"> Event Website ♫ </a> </span> </div> <div class="venue"> <span class="venue_name"> <a href="{% url 'concerts:venue_events' slug=result.venue.slug %}"> {{ result.venue.name }} </a> </span> <span class="address"> <a href="https://www.google.com/maps?q={{ result.venue.address }}" target="_blank"> {{ result.venue.address }} </a> </span> <span class="venue_website"> <a href="{{ result.venue.website }}" target="_blank"> Venue Website ♩ </a> </span> </div> </div> {% endfor %} {% endif %} </div> {% endblock %}
I borrowed heavily from my main page HTML here. There’s a lot going on here, but basically it just renders all the fields in the model the way I want. The header section shows the search term and number of results.
settings.py
INSTALLED_APPS = [ ... 'django.contrib.postgres',
One more thing! I used PostgreSQL’s full text search engine in my views.py
file. If I don’t add it to my installed apps, my site will throw an error.
CSS is beyond the scope of this post, but after lots of CSS wizardry, I’ve got desktop, tablet, and mobile versions of the search bar up and running:
And some sample search results for good measure:
There it is!