Search Bar in Django - django

EDIT: I managed to include a Search bar using 'DjangoFilter'
I would like to add a search bar to my template in Django
I would like to include a search box in above a list of articles so users can search through the data.
When I enter something in the bar nothing happens though...
here below my code
Thanks for the help in advance
In HTML page
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" name="search" >
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
In views.py
def article_overview(request):
search_term = ''
if 'search' in request.GET:
search_term = request.GET['search']
articles = Article.objects.all().filter(feeder__icontains=search_term)
articles = Article.objects.all()
return render(request, 'overviews/overview.html', {'articles' : articles, 'search_term': search_term })
in overview.html(simplified):
{% for article in articles %}
<a> {{article.feeder}} </a>
{% endfor %}

In views.py,
class SearchView(ListView):
model = Article
template_name = 'search.html'
context_object_name = 'all_search_results'
def get_queryset(self):
result = super(SearchView, self).get_queryset()
query = self.request.GET.get('search')
if query:
postresult = Article.objects.filter(title__contains=query)
result = postresult
else:
result = None
return result
In your template to search query,
<form class="add_your_class" method="GET" action="" >
<input class="add_your_class" type="search" name="search">
<button class="add_your_class" type="submit"> Search </button>
</form>
In templates you can add it as to show results
{% for result in all_search_results %}
{{ .....add according to your model }}
{% empty %}
add something to show no results
{% endfor %}

My example worked in my case:
base template:
form class="form-inline my-2 my-lg-0" method="GET" action="{% url 'ecomm:search' %}" >
<input class="form-control mr-sm-2" type="search" name="search">
<button class="btn btn btn-outline-info my-2 my-sm-0" type="submit"> Search </button>
</form>
search template:
{% extends "ecomm/base.html" %}
{% block content %}
{% for product in all_search_results %}
<h3>{{product.title}}</h3>
<p>{{product.description}}</p>
<img src="{{ product.picture.url }}">
{% empty %}
<h2>No results found</h2>
{% endfor %}
{% endblock %}
the view:
class SearchView(ListView):
model = Products
template_name = 'ecomm/search.html'
context_object_name = 'all_search_results'
def get_queryset(self):
result = super(SearchView, self).get_queryset()
query = self.request.GET.get('search')
if query:
postresult = Products.objects.filter(title__contains=query)
result = postresult
else:
result = None
return result
the url:
path('results/', ecomm_views.SearchView.as_view(), name='search'),

Another simple solution is to use DataTables which inlcude a search bar by default. Put your fields in each row and search it out that way.
<table id="articles" class="display" style="width:100%">
<thead>
<tr>
<th>Feeder</th>
</tr>
</thead>
<tbody>
{% for article in articles %}
<tr>
<td>{{article.feeder}</td>
</tr>
{% endfor %}
</tbody>
</table>
<script>
$(document).ready(function () {
$('#articles').DataTable();
});
</script>

Assuming that you have your articles saved in your database, what you can do is have a datalist for your input, use AJAX to prepopulate the datalist with the articles from your database so that you have some sort of an autocomplete dropdown/search box.
There's an excellent tutorial from Teamtreehouse.com for this implementation. Here's the link.

You are assigning article variable in two places, therefore you're overwriting the first one. Rename article variable inside if statement and pass that to template
if 'search' in request.GET:
search_term = request.GET['search']
search_result = Article.objects.all().filter(feeder__icontains=search_term)
articles = Article.objects.all()
return render(request, 'overviews/overview.html', {'articles' : articles, 'search_result': search_result })

Related

Django links, url parameters overwriting

I've got django template with table/list with sorting action:
Name
and later I've got paging:
first
What I want to achieve is a link:
http://localhost:8000/list/?page=3&order_by=name
but, while - clicking a link, I can get either page or order_by.
How to solve it the 'django way'? (for 2 and more parameters).
More details -
def list_view(request):
items_list = model.objects.all()
# Filter
request_filter = request.GET.get('filter',"")
if request_filter:
items_list = items_list.filter(model__icontains=request_filter)
# Paging
paginator = Paginator(list, 2) # Show 2 items per page.
page_number = request.GET.get('page',1)
page_obj = paginator.get_page(page_number)
items_list = paginator.page(page_number)
return render(request,'list.html', {'list' : items_list, 'filter':request_filter, 'page_obj': page_obj})
and template: (related parts)
<form action="" method="GET">
<input type="text" name="filter" value={{filter}} >
<button class="btn btn-sm btn-outline-success" type="submit">Filtruj</button>
</form>
<table class="table">
<thead>
<tr>
<th>Name</th>
etc </table>
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
« first
previous
{% endif %}

Django display search results

I'm currently working on a blog app in Django. As all Blog apps I need a search form. Therefore I have write a small view and context processor (to make the search form available globally) that queries search results:
view.py:
class BlogSearchListView(ListView):
model = Post
paginate_by = 10
def get_queryset(self):
qs = Post.objects.published()
keywords = self.request.GET.get('q')
if keywords:
query = SearchQuery(keywords)
title_vector = SearchVector('title', weight='A')
content_vector = SearchVector('content', weight='B')
tag_vector = SearchVector('tag', weight='C')
vectors = title_vector + content_vector + tag_vector
qs = qs.annotate(search=vectors).filter(search=query)
qs = qs.annotate(rank=SearchRank(vectors, query)).order_by('-rank')
return qs
base.html:
<div class="globalsearch">
<form id="searchform" action="{% url 'search' %}" method="get" accept-charset="utf-8">
<label for="{{ categorysearch_form.category.id_for_label }}">In category: </label>
{{ categorysearch_form.category }}
<input class="searchfield" id="searchbox" name="q" type="text" placeholder="Search for ...">
<button class="searchbutton" type="submit">
<i class="fa fa-search"></i>
</button>
</form>
</div>
settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.auth',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
'quickblog.quickblog.context_processors.categorysearch_form',
)
context_processors.py
from .forms import PostForm
def categorysearch_form(request):
form = PostForm()
return {'categorysearch_form': form}
post_list.html:
{% extends 'quickblog/base.html' %}
{% block content %}
{% for post in posts %}
<div class="post">
<h1><u>{{ post.title }}</u></h1>
<p>{{ post.content|linebreaksbr }}</p>
<div class="date">
<a>Published by: {{ post.author }}</a><br>
<a>Published at: {{ post.published_date }}</a><br>
<a>Category: {{ post.category }}</a><br>
<a>Tag(s): {{ post.tag }}</a>
</div>
</div>
{% endfor %}
<div>
<span>
{% if posts.has_previous %}
« First <a> |</a>
Previous
{% endif %}
{% if posts.has_next %}
<span> Page {{ posts.number }} of {{ posts.paginator.num_pages }}.</span>
Next<a> |</a>
Last »
{% endif %}
</span>
</div>
{% endblock %}
The rest of the project can be checked here: https://github.com/rsmvdl/quickblog
I now want to render the results the same way as they get display in my post_list.html view with one detail more: I want that the word the user searched for gets highlighted in the search results, so that the user is able to get the context of the content ASAP ;) . I hope to get any creative ideas. Thanks.
You can create a custom template filter to add an HTML tag around the searched-for word in your post title or content. This will not be an exact solution but you should be able to adapt it:
#register.filter
#stringfilter
def highlight_search_term(text, search_term):
if search_term in text:
text.replace(search_term, '<span class="highlight">' + search_term + '</span>')
return mark_safe(text)
You can then apply this filter to your post title or content in your template with {{ post.title|highlight_search_term:search_term }}.
You will need to also pass search_term to your template context so that the filter can know what to highlight. Be careful with mark_safe() if you are applying this to user-submitted content!
You can create a context processor and pass request.GET.get('q') as context.
context_processors.py
def search(request):
search_keyword = request.GET.get('q')
context = {'search_keyword': search_keyword}
return context
And in your template access it as {{ search_keyword }}
search_results.html
<h3>Showing results for: {{ search_keyword }}</h3>

In django, how to use another model data from function based views

So I have a form that updates a key_instance object with a borrower. Currently my app needs the user to enter the name of the borrower, but I want it to display a dropdown list of data from another model the user model to select from, is there anyway to do this in a class based view? Here are my views.py and my template. What I was thinking is that I would like to use a get_list_or_404 on the user model and display it as a drop down list in the template and use that selection to populate the form field.
I manged to get the dropdown list to display in my template but I'm not sure as to how to save that value in my views.
Does anyone know if this is the right way or if this is doable? Thank you!!
views.py
def submit_key_request(request, pk):
"""
View function for renewing a specific keyInstance by admin
"""
key_inst=get_object_or_404(KeyInstance, pk=pk)
names = get_list_or_404(Users)
# If this is a POST request then process the Form data
if request.method == 'POST':
# Create a form instance and populate it with data from the request (binding):
form = UpdateKeyForm(request.POST)
# Check if the form is valid:
if form.is_valid():
# process the data in form.cleaned_data as required (here we just write it to the model due_back field)
key_inst.is_requested = True
key_inst.status = 'r'
key_inst.date_requested = datetime.date.today()
key_inst.borrower = form.cleaned_data['borrower']
key_inst.save()
# redirect to a new URL:
return HttpResponseRedirect(reverse('all-available-keys') )
# If this is a GET (or any other method) create the default form.
else:
form = UpdateKeyForm(initial={'borrower': 'N/A'})
return render(request, 'catalog/keyinstance_request_update.html', {'form': form, 'keyinst':key_inst})
template
{% extends "base_generic.html" %}
{% block content %}
<div class="wrapper">
<div class="centered"> <h1>Request Keys For Room: {{keyinst.roomkey}}</h1></div>
<div class="square-box">
<div class="square-content">
<form action="" method="post" >
{% csrf_token %}
<table style="display: inline-flex">
{{ form}}
</table>
<select name = 'name'>
{% for name in names %}
<option value="{{ name }}">{{ name }}</option>
{% endfor %}
</select>
<p>
(Please use their login name i.e. <b>{{ user.get_username }}</b>)
</p>
<p><input required id="checkBox" type="checkbox" onclick="validate()"> I accept the terms and conditions</p>
<p id="text" style="display:none">You Have Agreed To the Terms and Conditions</p>
<input type="submit" value="Submit" />
</form>
</div>
</div>
</div>
{% endblock %}
Here is how I manged to do it, Not sure if this is the best 'pythonic' or best practice. Please let me know if it's not.
my views.py
def submit_key_request(request, pk):
"""
View function for renewing a specific keyInstance by admin
"""
key_inst=get_object_or_404(KeyInstance, pk=pk)
names = get_list_or_404(User)
# If this is a POST request then process the Form data
if request.method == 'POST':
name = request.POST['name']
key_inst.is_requested = True
key_inst.status = 'r'
key_inst.date_requested = datetime.date.today()
key_inst.borrower = name
key_inst.save()
return HttpResponseRedirect(reverse('all-available-keys') )
# If this is a GET (or any other method) create the default form.
else:
pass
return render(request, 'catalog/keyinstance_request_update.html', {'keyinst':key_inst, 'names':names})
template
{% extends "base_generic.html" %}
{% block content %}
<div class="wrapper">
<div class="centered"> <h1>Request Keys For Room: {{keyinst.roomkey}}</h1></div>
<div class="square-box">
<div class="square-content">
<form action="" method="post" >
{% csrf_token %}
</br>
<select name = 'name' required>
{% for key in names %}
<option value="{{ key }}">{{ key }}</option>
{% endfor %}
</select>
<p>
(Please use their login name i.e. <b>{{ user.get_username }}</b>)
</p>
<p><input required id="checkBox" type="checkbox" onclick="validate()"> I accept the terms and conditions</p>
<p id="text" style="display:none">You Have Agreed To the Terms and Conditions</p>
<input type="submit" value="Submit" />
</form>
</div>
</div>
</div>
{% endblock %}

django form template does not update

I have created a form and I POST selection with an action to go to specific url. Although, once directed the template does not update.
I need to hit Enter again and then obviously my request is lost so I get exception value get_context_data() takes exactly 2 arguments (1 given)
any ideas why ?
Below is my code and print screen of the problem.
url
url(r'^all_rooms/$', views.AllRoomsView.as_view(), name='roomsdetails'),
template
<form action="/blocks/all_rooms/" method="post" >
{% csrf_token %}
<ul>
{% for choice in form.my_choice_field.field.choices %}
<li>
<input type="radio" name="my_choice_field" value="{{choice.0}}"
{% ifequal form.my_choice_field.data choice.0 %}
checked="checked"
{% endifequal %}/>
<label for="">{{choice.1}}</label>
</li>
{% endfor %}
</ul>
<input type="submit" value="Submit" />
</form>
EDIT:
Below is my AllRoomsView class. I suspect this is the class where the problem is.
class AllRoomsView(ListView):
template_name = 'prostats/roomsdetail.html'
queryset = Room.objects.all()
def get_context_data(self, request, **kwargs):
context = super(AllRoomsView, self).get_context_data(**kwargs)
context['rooms'] = Room.objects.all()
context['rlog'] = RoomLog.objects.all()
roomsdates = []
for r in context['rlog']:
if r not in roomsdates:
roomsdates.append(r.update_date)
roomextracted = []
for i in roomsdates:
if i not in roomextracted:
roomextracted.append(i)
context['roomextracted'] = roomextracted
choosend = '2016-02-16'
context['roomfiltersettime'] = RoomLog.objects.filter(update_date__lte = choosend)
return context

Django modelformset_factory saves nothing

(I'm quite new at django, i googled for this a lot but could not find an answer, so i hope you don't mind the low level question)
There are these Classes:
class Cl(models.Model):
user = models.ForeignKey(User,editable=False)
title = models.CharField(max_length=100)
class Member(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
cl = models.ForeignKey(Cl)
class ClForm(ModelForm):
class Meta:
model = Cl
I want to make a page where i can create an Object of Cl and some related Member Objects (I plan to use django-dynamic-formset but that is not the point here). My view looks like this:
class NewView(TemplateView):
def get(self, request):
cform = ClForm(prefix="cl", instance=Cl(user=request.user))
MemberFormSet = modelformset_factory(Member)
memberfs = MemberFormSet(queryset=Member.objects.none())
return render_to_response(self.template_name, {'cl_form': cform, 'memberformset': memberfs}, context_instance=RequestContext(request))
def post(self,request):
cform = ClForm(request.POST, prefix="cl", instance=Cl(user=request.user))
MemberFormSet = modelformset_factory(Member)
memberfs = MemberFormSet(request.POST)
if cform.is_valid() and memberfs.is_valid():
new_cl = cform.save()
new_members = memberfs.save(commit=False)
for mf in new_members:
mf.cl = new_cl
mf.save()
return HttpResponseRedirect("unimportant")
return render_to_response(self.template_name, {'cl_form': cform, 'memberformset': memberfs}, context_instance=RequestContext(request))
My template looks like this:
<form id="myForm" method="post" action="">
{% csrf_token %}
{{ memberformset.management_form }}
<table>
{{ cl_form }}
<tr><th colspan="2">Members</th></tr>
{% with memberformset.empty_form as form %}
<div id="id_empty_form" style="display:none;">
{{ form }}
</div>
{% endwith %}
</table>
<input type="submit" value="Save" />
</form>
My problem is now, that the member is not stored. Also, the memberfs.is_valid() doens't complain, if e.g. the Email field is not filled out correctly.
I also tried the inlineformset_factory, but that didn't worked either.
I think the problem is that you are using the "empty_form" in that template. The correct way of using the formset is described here. In your example, you should write:
<form id="myForm" method="post" action="">
{% csrf_token %}
{{ memberformset.management_form }}
<table>
{{ cl_form }}
<tr><th colspan="2">Members</th></tr>
<!-- THIS IS NOT NECESSARY UNLESS YOU WANT TO ADD FORMS WITH JAVASCRIPT-->
<div id="id_empty_form" style="display:none;">
{{ memberformset.empty_form }}
</div>
<!-- END NOT NECESSARY-->
{% for form in memberformset.forms %}
{{ form }}
{% endfor %}
{% endwith %}
</tbody>
</table>
<input type="submit" value="Save" />
</form>