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!