Searching Snippets
Last updated on July 27, 2020
In this lesson, we will add a view to allow users to search snippets via keywords.
Let's start by creating a search form.
In the forms.py
, define SearchForm
class towards the end of the file as follows:
djangobin/django_project/djangobin/forms.py
1 2 3 4 5 6 | #...
class SearchForm(forms.Form):
query = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control',
'placeholder': 'Search'}))
mysnippet = forms.BooleanField(required=False)
|
The query
field is where the users will enter their search term. The mysnippet
field will be visible only to the users who are logged in. If selected, it allows users to search through his snippets only.
Next, add a new view named search()
in views.py
just below the profile()
view as follows:
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #...
from django.db.models import Q
from .forms import SnippetForm, ContactForm, LoginForm, CreateUserForm, \
SettingForm, SearchForm
#...
def search(request):
f = SearchForm(request.GET)
snippets = []
if f.is_valid():
query = f.cleaned_data.get('query')
mysnippets = f.cleaned_data.get('mysnippet')
# if mysnippet field is selected, search only logged in user's snippets
if mysnippets:
snippet_list = Snippet.objects.filter(
Q(user=request.user),
Q(original_code__icontains=query) | Q(title__icontains=query)
)
else:
qs1 = Snippet.objects.filter(
Q(exposure='public'),
Q(original_code__icontains = query) | Q(title__icontains = query)
# Q(user=request.user)
)
# if the user is logged in then search his snippets
if request.user.is_authenticated:
qs2 = Snippet.objects.filter(Q(user=request.user),
Q(original_code__icontains=query) | Q(title__icontains=query))
snippet_list = (qs1 | qs2).distinct()
else:
snippet_list = qs1
snippets = paginate_result(request, snippet_list, 5)
return render(request, 'djangobin/search.html', {'form': f, 'snippets': snippets })
|
This view function works as follows:
In line 9, we instantiate a
SearchForm
instance by passingrequest.GET
data to form constructor. The reason we are binding data to the form at the outset is that thesearch()
function will be will be called only when the user has submitted the query in the search box at the top of the page. Further, the query is submitted using the GET request this allows users to bookmark the search if they want to.In line 12, we use
is_valid()
method to determine whether the form is valid or not. If the form is not valid, we render an empty form without any data; otherwise, the course of action depends upon how the form is submitted. There are two possible scenarios:
If the form is submitted with mysnippet
field checked, then this will trigger the execution of the following if block.
1 2 3 4 5 6 7 8 | #...
# if mysnippet field is selected, search only logged in user's snippets
if mysnippets:
snippet_list = Snippet.objects.filter(
Q(user=request.user),
Q(original_code__icontains=query) | Q(title__icontains=query)
)
#...
|
On the other hand, If the form is submitted without the mysnippet
field checked, then this will trigger the execution of the else block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #...
else:
qs1 = Snippet.objects.filter(
Q(exposure='public'),
Q(original_code__icontains = query) | Q(title__icontains = query)
# Q(user=request.user)
)
# if the user is logged in then search his snippets
if request.user.is_authenticated:
qs2 = Snippet.objects.filter(Q(user=request.user),
Q(original_code__icontains=query) | Q(title__icontains=query))
snippet_list = (qs1 | qs2).distinct()
else:
snippet_list = qs1
#...
|
If the user is logged in, we create a new queryset containing the snippets created by the user. The querysets qs1
and qs2
may contain duplicate results. To remove the duplicates combine two querysets using |
(bitwise OR) operator and then apply the distinct()
method on the resulting queryset. We are now left with unique results.
In line 40, we call paginate_result()
to get paginated results.
Finally, in line 42, we render the template.
Add a URL pattern named search
to urls.py
as follows:
djangobin/django_project/djangobin/urls.py
1 2 3 4 5 6 | #...
urlpatterns = [
#...
url('^delete/(?P<snippet_slug>[\d]+)/$', views.delete_snippet, name='delete_snippet'),
url('^search/$', views.search, name='search'),
]
|
Create a template named search.html
and add the following code to it:
djangobin/django_project/djangobin/templates/djangobin/search.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | {% extends 'djangobin/base.html' %}
{% block title %}
{{ request.GET.query }} - {{ block.super }}
{% endblock %}
{% block main %}
<form action="" class="form-inline">
<div class="form-group">
{{ form.query }}
</div>
{% if request.user.is_authenticated %}
<div class="checkbox">
<label>
{{ form.mysnippet }} Only search my snippets.
</label>
</div>
{% endif %}
<button type="submit" class="btn btn-primary">Search</button>
</form>
<hr>
{% for snippet in snippets %}
{% if forloop.first %}
<h5>{{ snippets.paginator.count }} record{{ snippets.paginator.count|pluralize }} found.</h5>
<hr>
{% endif %}
<h4><a href="{{ snippet.get_absolute_url }}">{{ snippet.title }}</a></h4>
<p>{{ snippet.original_code|truncatechars:250 }}</p>
<hr>
{% empty %}
<h5>No records found.</h5>
{% endfor %}
{% if snippets.paginator.num_pages > 1 %}
<nav aria-label="...">
<ul class="pager">
<li>Page {{ snippets.number }} of {{ snippets.paginator.num_pages }}</li>
{% if snippets.has_previous %}
<li><a href="?query={{ request.GET.query }}&page={{ snippets.previous_page_number }}">Previous</a></li>
{% endif %}
{% if snippets.has_next %}
<li><a href="?query={{ request.GET.query }}&page={{ snippets.next_page_number }}">Next</a></li>
{% endif %}
</ul>
</nav>
{% endif %}
{% endblock %}
|
Next, update <form>
element base.html
as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | {# ... #}
<li {% if request.path == '/about/' %}class='active'{% endif %}>
<a href="">About</a>
</li>
<li {% if request.path == '/contact/' %}class='active'{% endif %}>
<a href="{% url 'djangobin:contact' %}">Contact</a>
</li>
</ul>
<form action="{% url 'djangobin:search' %}" class="navbar-form navbar-left" method="get">
<div class="form-group">
<input type="text" name="query" class="form-control"
placeholder="Search" value="{{ request.GET.query }}">
</div>
</form>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
aria-haspopup="true" aria-expanded="false">
{% if request.user.is_authenticated %}
{{ request.user.username|upper }}
{% else %}
{# ... #}
|
Now, open your browser and navigate to http://localhost:8000/
. Enter a query in the search box at the top of the page and hit enter. You should see search results as follows:
If you are logged in then you will see mysnippet
select box beside the search box as follows:
Submitting the form with mysnippet
field checked will limit the search results to your snippets only.
Load Comments