Django - How to load paginated Page (next page) into my Template? - django

I have a model named "Person" (List of names).
6 Persons named "Anne". My Pagination is 2 per Page.
When i start a search query on my page for "Anne". I get the Response of "6 Results", it shows me the first two Results and it displays "Page 1 of 3. Next. So far everything fine. but when i press "next" the Browser only updates to
..../post_searchtwo/?page=2 but the next two Results will not show up.
(Using Django version 2.2.2 with PostgreSQL 11)
thanks in advance.
views.py
def post_searchtwo(request):
form = SearchForm()
query = None
results = []
if 'query' in request.GET:
form = SearchForm(request.GET)
if form.is_valid():
query = form.cleaned_data['query']
results = Person.objects.annotate(
search=SearchVector('city', 'last_name', 'first_name')
).filter(search=query)
paginator = Paginator(results, 2) # --- Show 2 items per page
page = request.GET.get('page')
try:
post_list = paginator.page(page)
except PageNotAnInteger:
post_list = paginator.page(1)
except EmptyPage:
post_list = paginator.page(paginator.num_pages)
context = {
'form': form,
'query': query,
'page': page,
'post_list': post_list,
'results': results
}
return render(request,'search/post_searchtwo.html', context)
my template / post_searchtwo.htnl
{% extends 'base.html' %}
{% load static %}
{% block custom_css %}
<link rel="stylesheet" type="text/css" href="{% static 'css/home_styles.css' %}">
{% endblock %}
{% block content %}
<form action="." method="get">
{{ form.as_p }}
<input type="submit" value="Search">
</form>
{% if query %}
<h3>Posts containing "{{ query }}"</h3>
<h3>
{% with results.count as total_results %}
Found {{ total_results }} result{{ total_results|pluralize }}
{% endwith %}
</h3>
{% for post in post_list %}
<h4>
{{ post.first_name }} {{ post.last_name }}
</h4>
{% empty %}
<p>Theren are no results for your query.</p>
{% endfor %}
<div class="col-6 offset-md-3">
{% include "pagination.html" with page=post_list %}
</div>
{% endif %}
{% endblock %}
.../pagination.htnl
<div class="pagination">
<span class="step-links">
{% if page.has_previous %}
Previous
{% endif %}
<span class="current">
Page {{ page.number }} of {{ page.paginator.num_pages }}.
</span>
{% if page.has_next %}
Next
{% endif %}
</span>
</div>
Update: Thanks to #Daniel Roseman
as Daniel pointed out (down below) here the changes.
.../pagination.htnl
<div class="pagination">
<span class="step-links">
{% if page.has_previous %}
Previous
{% endif %}
<span class="current">
Page {{ page.number }} of {{ page.paginator.num_pages }}.
</span>
{% if page.has_next %}
Next
{% endif %}
</span>
</div>

Related

Django - pagination based on search criteria

I am having issues getting results when clicking on page 2 and above - most likely due to url issues. I have a list of names and if I search on e.g. "John" I want them to be separated by pages if number of names > e.g. 10.
My Views are as follows: (searching works fine)
def name_search(request):
if 'q' in request.GET:
q=request.GET.get('q', '')
pvt_list = pvt_data.objects.filter(full_name__icontains=q)
#Pagination
p = Paginator(pvt_list, 10) # Show 10 contacts per page.
page_num = request.GET.get('page', 1)
try:
page = p.page(page_num)
except PageNotAnInteger:
page = p.page(1)
except EmptyPage:
page = p.page(1)
context = {'items' : page}
return render(request, 'home/name_search.html', context)
else:
return render(request, 'home/name_search.html')
My urls.py file is
urlpatterns = [
...
path('name_search', views.name_search, name='name_search'),
...
]
My html file is
{% for pvt in items %}
{{ pvt.full_name }}
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if items.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ items.number }} of {{ items.paginator.num_pages }}.
</span>
{% if items.has_next %}
next
{% endif %}
</span>
</div>
When I search, I get the following link 'http://127.0.0.1:8000/name_search?q=John' with the first 10 names correct.
When I click on next button I get the following link:
http://127.0.0.1:8000/name_search?page=2
Changing the link manually to
http://127.0.0.1:8000/name_search?q=John/page=2
does not work...
Any response is appreciated :-)
a solution might be to add your search parameter to your href.
so instead of:
next
try:
next
You are almost there, you need to pass the query to the next/previous pages as well.
{% for pvt in items %}
{{ pvt.full_name }}
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if items.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ items.number }} of {{ items.paginator.num_pages }}.
</span>
{% if items.has_next %}
next
{% endif %}
</span>
</div>

How to create and submit multiple instances of a form on a single page?

I want 3 instance of a URL input form so I can submit up to 3 different URLs.
forms.py
class AdditemForm(forms.Form):
url = forms.URLField(
label='Add Item',
widget=forms.URLInput(
attrs={
"class": "form-control",
}))
view.py
def ItemDetail(request, pk):
listitem = comparelist.objects.get(id=pk)
if request.method == 'POST':
form = AdditemForm(request.POST)
if form.is_valid():
url = form.cleaned_data.get("url")
items.objects.create(
link=url,
name=product_name,
price=price,
store=store,
)
return HttpResponseRedirect(request.path_info)
else:
form = AdditemForm()
template = 'itemdetail.html'
context = {
"comparelist": listitem,
"form": form,
}
return render(request, template, context)
I'm using a form snippet I found in a tutorial:
{% load widget_tweaks %}
<form method="post" class="form">
{% csrf_token %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% if form.non_field_errors %}
<div class="alert alert-danger" role="alert">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
{% for field in form.visible_fields %}
<div class="form-group">
{{ field.label_tag }}
{% if form.is_bound %}
{% if field.errors %}
{% render_field field class="form-control is-invalid" %}
{% for error in field.errors %}
<div class="invalid-feedback">
{{ error }}
</div>
{% endfor %}
{% else %}
{% render_field field class="form-control is-valid" %}
{% endif %}
{% else %}
{% render_field field class="form-control" %}
{% endif %}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
So how do I get 3 of those forms on my page and be able to submit 3 different URLs?
I can only think of having to create 3 different form classes and paste the form snippet 3 times into the template. But that seems like a lot of unnecessary repetition.
Why "create 3 different form classes" ??? You can just create three instances of the same form.
paste the form snippet 3 times into the template
Ever heard about lists and loops ? You can put the three (or more) forms in a list and loop over it.
BUT that's actually not the best solution here - Django has Formsets for this use case.

Get Django search query right

Im currently facing a problem since a couple of days for now. I simply want to implement a search view into my Django app. But when i try to search something on my App i get the following error:
init() takes 1 positional argument but 2 were given__init__() takes
In the end i want that my Query is a combination of category and searchword. So that the user can filter specific categories (Just like Amazon.com searchfield) e.g.: http://127.0.0.1:8000/search/?category=1&q=hallo
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>
</div>
...
categorysearch_form is a dropdown selector that gets his ID from the Database.
views.py
...
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
from django.views.generic import ListView
class globalsearch(ListView):
"""
Display a Post List page filtered by the search query.
"""
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
...
urls.py
...
url(r'^search/$', views.globalsearch.as_view(), name='search'),
...
Search.html results are getting displayd here:
{% extends 'quickblog/base.html' %}
{% block content %}
{% for post in object_list %}
<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 %}
{% if is_paginated %}
<ul class="pagination">
{% if page_obj.has_previous %}
<li>«</li>
{% else %}
<li class="disabled"><span>«</span></li>
{% endif %}
{% for i in paginator.page_range %}
{% if page_obj.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% else %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li>»</li>
{% else %}
<li class="disabled"><span>»</span></li>
{% endif %}
</ul>
{% endif %}
{% endblock %}
Project: https://github.com/rsmvdl/quickblog
Since globalsearch is class based view it should be globalsearch.as_view() in your urls:
url(r'^search/$', views.globalsearch.as_view(), name='search'),

How to store form data in form pagination using session?

I'm working on a quiz. The quiz has multiple questions - forms. Since there may be many questions, I've created a pagination for these forms. Everything seems to work correctly but only the data from forms on last page are sent to server.
How to hold the data from previous pages so I can send all forms then to server?
I'm considering using session but can't figure out how to make it work. As you can see, all questions are in one form with method post. But pagination are GET methods.
def take_quiz(request,id):
if request.method == 'POST':
quiz = get_object_or_404(models.LanguageQuiz, pk=id)
sitting = get_object_or_404(models.Sitting, quiz=quiz, user=request.user)
for k,v in request.POST.iteritems():
if 'choice-for' in k:
q = Question.objects.get(pk=k.split('-')[-1])
choice = Choice.objects.get(pk=v)
sitting_question = get_object_or_404(SittingQuestion,sitting=sitting,question=q)
sitting_question.answer = choice
sitting_question.save()
correct_answers = len([x for x in sitting.sitting_questions.all() if x.answer.correct])
result = float(correct_answers)/sitting.sitting_questions.all().count() * 100
return render(request,'quiz/result.html',context={'result':result,
'level':level,
'quiz':quiz})
if request.method == 'GET':
with transaction.atomic():
quiz = get_object_or_404(models.LanguageQuiz, pk=id)
if models.Sitting.objects.filter(quiz=quiz, user=request.user).exists():
sitting = get_object_or_404(models.Sitting, quiz=quiz, user=request.user)
check_expired_sitting(sitting)
sitting.delete()
sitting = models.Sitting.objects.create(quiz=quiz, user=request.user)
sitting.load_questions()
questions = [x.question for x in sitting.sitting_questions.all()]
paginator = Paginator(questions,2)
page_num = request.GET.get('page',1)
page = paginator.page(page_num)
context = {'page':page,
'quiz':quiz}
return render(request,'quiz/quiz.html',context=context)
TEMPLATE
{% extends 'base.html' %}
{% block content %}
<h1>{{ quiz.name }}</h1>
<br><br><br>
<form action="" method="post">{% csrf_token %}
{% for question in page.object_list %}
<div class="question">
<h3>{{ question.text }}</h3>
<div class="choices">
{% for choice in question.get_choices_list %}
<input type="radio" name="choice-for-question-{{ question.id }}"
id="choice-{{ question.id }}-{{ forloop.counter }}" value="{{ choice.id }}"/>
<label for="choice-{{ question.id }}-{{ forloop.counter }}">{{ choice.text }}</label><br/>
{% endfor %}
</div>
</div>
{% endfor %}
{% if not page.has_next %}
<button class='accordion' type="submit">Submit Quiz</button>
{% endif %}
</form>
<div class="pagination">
<span class="step-links">
{% if page.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ page.number }} of {{ page.paginator.num_pages }}.
</span>
{% if page.has_next %}
next
{% endif %}
</span>
</div>
{% endblock %}

Django comments pagination isnt working

there is a trouble I'm new to django and there is an issue I can't understand,
there is a view:
def article(request, article_id = 1, comments_page_number = 1):
all_comments = Comments.objects.filter(comments_article_id = article_id)
paginator = Paginator(all_comments, 2)
comment_form = CommentForm
args = {}
args.update(csrf(request))
args['article'] = Article.objects.get(id = article_id)
args['comments'] = paginator.page(comments_page_number)
args['form'] = comment_form
args['username'] = auth.get_user(request).username
return render_to_response('article.html', args)
there is a template article.html
{% extends 'main.html' %}
{% block article %}
<h4>{{article.article_date}}</h4>
<h2>{{article.article_title}}</h2>
<p> {{article.article_body}}</p>
<hr>
<div class="large-offset-1 large-8 columns">
<p>Комментарии: </p>
{% for comment in comments %}
<p>{{comment.comments_text}}</p>
<hr>
{% endfor %}
{% if username %}
<form action="/articles/addcomment/{{article.id}}/" method="POST" >
{% csrf_token %}
{{form }}
<input type="submit" class="button" value="Add comment">
</form>
{% endif %}
</div>
<div class="row">
<div class="large-3 large-offset-5 columns">
<ul class="pagination">
{% if comments.has_previous %}
<li class="arrow">«</li>
{% else %}
<li class="arrow unavailable">«</li>
{% endif %}
{% for page in comments.paginator.page_range %}
{% if page == comments.number %}
<li class="current">{{ page }}</li>
{% else %}
<li>{{ page }}</li>
{% endif %}
{% endfor %}
{% if comments.has_next %}
<li class="arrow">»</li>
{% else %}
<li class="arrow unavailable">»</li>
{% endif %}
</ul>
</div>
</div>
{% endblock %}
this is my article/urls.py
urlpatterns = patterns('',
url(r'^articles/get/(?P<article_id>\d+)/$','article.views.article'),
url(r'^articles/get/(?P<article_id>\d+)/comments/(\d+)/$', 'article.views.article'),
)
after that on my article page appeared an pages pagination, but when I'm clicking on the second page, for example, it it is just changing my url, but new comments are not appearing, just old ones.
What should I do to do this right? Thank you very much!
Your variable name comments_page_number uses always the default value. Name your second parameter in the url route to match this variable name.
you need :
url(r'^articles/get/(?P<article_id>\d+)/comments/(?P<comments_page_number>\d+)/$', 'article.views.this_article'),