Django Search Page with Query Pagination - html

Hello i have implemented a simple search form and search view to show search result. Now i want to paginate them. But there is a problem with the page in the url. My search url looks like ../search?q=Bla
Now if i try to add pagination like: ../search?q=Bla?page=2 (at least thats how i understand it would work) it takes the whole string after q= to my database filter. I took a look at how stackoverflow handles searching and pagination and found out using '&' here is the view code:
def search(request):
# get query search parameters
page = request.GET.get('page', 1)
query = request.GET.get('q', '')
# query none or empty
if query is None or query == '':
return redirect('home')
# query valid
else:
# icontains make sure to ignore character sensitivity
post_list = Post.objects.filter(title__icontains=query)
paginator = Paginator(post_list, 5)
try:
posts_l = paginator.page(page)
except PageNotAnInteger:
posts_l = paginator.page(1)
except EmptyPage:
posts_l = paginator.page(paginator.num_pages)
return render(request, 'search.html', {'post_list': posts_l, 'query': query})
and here the HTML Snippet:
{% if post_list.paginator.num_pages > 1 %}
<div class="pagination">
<span class="step-links mb-5 mx-auto">
{% if post_list.has_previous %}
<a class="mr-3" href="?page={{ post_list.previous_page_number }}&q={{ query }}">zurück</a>
{% endif %}
<span>Seite {{ post_list.number }} von {{ post_list.paginator.num_pages }}</span>
{% if post_list.has_next %}
<a class="ml-3" href="?page={{ post_list.next_page_number }}&q={{ query }}">nächste</a>
{% endif %}
</span>
</div>
{% endif %}
So now the url is build like search?q=Test for the first page. And for the other pages (which suprisingly works) is search?page=2&q=Test. Now im happy it works but i dont quite how just adding &q={{ query }} solved my problem. Is this some kind of universal RFC? I dont quite understand since i was just checking out how this side does it. implemented it and works?

A query string [wiki] is the part after the question mark (?) of a URL. It is a string that consists out of a sequence of key-value pairs separated by an ampersand (&). The key and the value are separated by the equals sign (=). Both the key and the value are percent-encoded [wiki]. So as you found out:
page=2&q=Test
is a querystring that contains two key-value pairs: page maps to 2 and q to test.
The code is however not entirely "safe". If the query itself contains an ampersand &, etc. then this can result in an incorrect query. You should make use of the |urlencode template filter [Django-doc] to percentage encode the value:
<a class="ml-3" href="?page={{ post_list.next_page_number }}&q={{ query|urlencode }}">nächste</a>

Related

Django How to make the condition correct on an HTML page - search bar

I try to do a search engine if the word in my DB thah I created then display the word on the HTML page and if not then nothing.. I did it right in VIEW but I can not apply it on the HTML page I searched the internet and did not find an answer I'm sure I fall for something stupid.
This is the view
def Search_word(request):
search = request.POST.get("search") #Grab the search item
return render(request,"search_page.html", {"search":search})
this is the html:
{%for i in Word.English_word%}
{%if search in Word.English_word%}
{{search}}
{%endif%}
{%endfor%}
and the urls:
path("Search_page",views.Search_word ,name="Search-page"),
models:
class Words(models.Model):
English_word = models.CharField(max_length=30)
Hebrew_word = models.CharField(max_length=30)
How_To_Remember = models.CharField(max_length=40)
Name = models.CharField(max_length=20)
pub_date = models.DateTimeField()
The problem is that even if the word is valid it does not show me anything ..
You should implement the filtering logic in the view, not in the template. Templates are for rendering logic, not business logic. Furthermore one should filter with the database, since databases are designed to do this.
The view thus looks like:
def Search_word(request):
search = request.POST.get('search')
items = Word.objects.filter(English_word__contains=search)
return render(
request,
'search_page.html',
{'search': search, 'items': items}
)
and then in the template we render this with:
{% for item in items %}
{{ item.English_word }}: {{ item.Hebrew_word }} <br>
{% endfor %}
You can use as lookup __contains to check if the English_word has a substring that is equal to search, with __icontains you check case-insensitive, with __iexact you look for Words that match search case-insensitive, and finally you can filter with Engish_word=search for an exact match.

Need help creating search view and how to apply in the HTML page from DB that I created - Django

I am building a website with DJANGO where people put words and association how to remember the word.
I built the part of inserting the word.
I am now stuck in the part of implementing the word search and extracting the information from the DB.
def Search_word(request):
if request.method == "POST":
search = request.POST["search"]
word = Words.objects.filter(English_word__contains = search)
return render(request,"search_page.html",{word:word , search:search})
else:
return render(request,"search_page.html",{})
{% if search %}
<h1> You searched for {{search}}</h1>
{% for Words in search %}
{{Words.English_word}}
{{ Words.Hebrew_word }}
{{ Words.How_To_Remember}}
{% endfor %}
{% else %}
<h1> Hey! You Forgot To Search </h1>
{% endif %}
if you want to search only in one field, Django supports search which is much better and more accurate than contains
words = Words.objects.filter(English_word__search = search_term)
but if you like to have some sort of search engine-like functionality you can use PostgreSQL with Django's full text search feature
in model.py add these commands
from django.contrib.postgres.search import SearchVector
in your model manger:
def full_search(self, search_term):
return self.annotate(
search=SearchVector('English_word') +
SearchVector('blog__tagline')).filter(search=seach_term)
this method will remove <in/at/is/the/...> other English words and only search with word-to-vector algorithms
if you have to work with ranking the search results:
from django.contrib.postgres.search import SearchQuery, SearchRank,SearchVector
vector = SearchVector('english_word')
query = SearchQuery(search_term)
Word.objects.annotate(rank=SearchRank(vector, query)).order_by('-rank')
this will annotate the results based on their likelihood to search term.
by the way It's best practice to use Model Mangers and avoid doing ORM stuff in views.
Django Docs on search:
https://docs.djangoproject.com/en/3.2/ref/contrib/postgres/search/

Loop in django with 2 parameter [duplicate]

This question already has answers here:
is there a way to loop over two lists simultaneously in django?
(6 answers)
Closed 6 years ago.
I am new to Django, I have passed two list(rawmaterial and food) to my template, and then I want to have a loop like this :(it is the logic of my aim, the syntax is not correct)
for(i=0;i<food.length;i++)
<div ,id="menuFood>
<h4> food.name(i)</h4>
<h4> rawmaterial.name(i)</h4>
</div>
but when i searched, i can find only loop like this:
{% for o in some_list %}
{% endfor %}
so with this syntax , I can't understand how to create that loop. I think only nested loop can make by this syntax..
here is my view code :
def foods(request):
food = Food.objects.all()
raw = [];
.
.
.
raw.append(warehouse)
return render(request, 'polls/foods.html', {'food': food,'rawmaterial': raw})
You can't do index on django template, but you could just put 2 lists together in your views.py using zip function:
food = Food.objects.all()
raw = []
# ...
raw.append(warehouse)
result = zip(food, raw)
return render(request, 'polls/foods.html', {'result': result})
Then in your template:
{% for food, raw in result %}
<h4>{{ food }}</h4>
<h4>{{ raw }}</h4>
{% endfor %}
By the way, you seems to come from java/c++ background because in python people never do:
for(i=0; i<food.length; i++)
print food[i]
instead, we do:
for i in food:
print i
Django template is adopting the similar syntax, it makes writing a loop a lot easier.

Working with links in Django

I am working in a small blog application using Django. Sorry if the question is obvious, but I am a newbie. Actually it is my third since I started an online course. I have the following Queryset:
def all(request):
allTiles = Post.objects.values('title')
allPosts = Post.objects.all()[:3]
context = {'Posts': allPosts,"Titles":allTiles}
template = "home.html"
return render(request, template, context)
and the follwing html code:
<ol class="list-unstyled">
{% for singleTile in Titles %}
<li>{{singleTile.title}}</li>
{% endfor %}
</ol>
As you can see every title creates an link. Lets assume a person decides to read one of the posts. How can I use the title name and send a request back to the database to get the content of the post.
It is better to use the id or slug field for such task.
But if you surely want to use the title as the GET parameter then apply the urlencode filter to the field's value:
<a href="{% url 'post_detail' %}?title={{ singleTile.title|urlencode }}">
{{ singleTile.title }}
</a>
And the view will be something like this:
def post_detail(request):
post = get_object_or_404(Post, title=request.GET.get('title'))
return render(request, 'post_detail.html', {'post': post})
UPDATE: If you decide to go with the id/slug option then you can use the generic DetailView:
<a href="{% url 'post_detail' singleTile.id %}">
{{ singleTile.title }}
</a
urls.py:
from django.views.generic.detail import DetailView
from app.models import Post
url(r'^post/(?P<pk>\d+)/$', DetailView.as_view(model=Post),
name='post_detail')
You have to configure url first like
{% url 'app.views.post_id' singleTile.id %}</li>
In your urls
url(r'^post/(?P<post_id>\d+)/$', views.by_id, name='post_id'),
And in your views
def post_id(request, post_id):
allTiles = Post.objects.get(id=post_id)
return render(request, template, context)

Create hyperlink in django template of object that has a space

I am trying to create a dynamic hyperlink that depends on a value passed from a function:
{% for item in field_list %}
<a href={% url index_view %}{{ item }}/> {{ item }} </a> <br>
{% endfor %}
The problem is that one of the items in field_list is "Hockey Player". The link for some reason is dropping everything after the space, so it creates the hyperlink on the entire "Hockey Player", but the address is
http://126.0.0.1:8000/Hockey
How can I get it to go to
http://126.0.0.1:8000/Hockey Player/
instead?
Use the urlencode filter.
{{ item|urlencode }}
But why are you taking the name? You should be passing the appropriate view and PK or slug to url which will create a suitable URL on its own.
Since spaces are illegal in URLs,
http://126.0.0.1:8000/Hockey Player/
is unacceptable. The urlencode filter will simply replace the space with %20, which is ugly/inelegant, even if it does kind of get the job done. A much better solution is to use a "slug" field on your model that represents a cleaned-up version of the title field (I'll assume it's called the title field). You want to end up with a clean URL like:
http://126.0.0.1:8000/hockey_player/
To make that happen, use something like this in your model:
class Player(models.Model):
title = models.CharField(max_length=60)
slug = models.SlugField()
...
If you want the slug field to be pre-populated in the admin, use something like this in your admin.py:
class PlayerAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("title",)}
....
admin.site.register(Player,PlayerAdmin)
Now when you enter a new Player in the admin, if you type "Hockey Player" for the Title, the Slug field will become "hockey_player" automatically.
In the template you would then use:
{% for item in field_list %}
<a href={% url index_view %}{{ item.slug }}/> {{ item }} </a> <br>
{% endfor %}
There is this builtin filter .
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#urlencode
Although you should be using one of these
http://docs.djangoproject.com/en/dev/ref/models/fields/#slugfield