How to create pagination in Django? - django

I follow this Document of djangoproject.com : https://docs.djangoproject.com/en/1.8/topics/pagination/. But it is too simple. It is only Next and Previous button.
Now I want create pagination with more features such as http://i.imgur.com/ZiFeAqG.jpg.
This is code:
View.py
def hire(request):
hire_article_list = hire_article.objects.all().order_by('-id')
#hire_article_list = hire_article.objects.order_by("-publication_date")
paginator = Paginator(hire_article_list, 2) # Show 25 contacts per page
page = request.GET.get('page')
try:
hire_article_s = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
hire_article_s = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
hire_article_s = paginator.page(paginator.num_pages)
#return render_to_response('hire/list.html', {"page_list": page_list})
context = {'hire_article_s': hire_article_s}
return render(request, 'hire/list.html', context)
list.html
{% for j in hire_article_s %}
{# Each "j" is a page_list model object. #}
<li>{{ j.hiring}}</li>
{% endfor %}
{% if hire_article_s.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ hire_article_s.number }} of {{ hire_article_s.paginator.num_pages }}.
</span>
{% if hire_article_s.has_next %}
next
{% endif %}
</span>
</div>

I had a similar need last week and found this super useful gist (https://gist.github.com/jsatt/8183993) that worked fine (though I'm not sure why it wouldn't work till I put request in the function parameters). It's a subclass of django's Paginator function. You could put this in a utility file and call it whenever you want to use Paginate with the range.
For instance, I had mine in a file called utils.py, which is in my core app.
views.py
from core.utils import paginate
def hire(request):
hire_article_list = hire_article.objects.all().order_by('-id')
'''
Show 25 contacts per page, with a page range of 5, which means if you are
on page 8, it shows links to pages 6,7,8,9,10.
'''
hire_article_s = paginate(request, hire_article_list, 25, 5)
context = {'hire_article_s': hire_article_s}
return render(request, 'hire/list.html', context)
list.html
{% if hire_article_s.has_previous %}
previous
{% endif %}
{% for range in hire_article_s.neighbor_range %}
{% if range == hire_article_s.number %}
<li class="pagination__item active ">{{ range }}</li>
{% else %}
<li class="{% if range == hire_article_s.number %}active {% endif %}">{{ range }}</li>
{% endif %}
{% endfor %}
{% if hire_article_s.has_next %}
next
{% endif %}
Hope this helps.
UPDATE
The above code has been edited a bit. I've added the context and the template format. Note that I'm using a loop to go through {{ hire_article_s.neighbor_range }} and print out the page numbers. I also do a check to highlight the current page's number. the This should work, as it's pretty much my own code with your own variable names.

Related

Pagination Elasticsearch in Django

I am a newbie in Elasticsearch, I just try to create a search engine using it in Django. Overall, the engine shows good results. Unfortunately, it loads a large number of the results. Then, I try to paginate it by regular pagination in Django, after that, the page load error object of type 'Search' has no len().
These are my codes:
view.py
from django.shortcuts import render, redirect
from elasticsearch import Elasticsearch
from elasticsearch_dsl import Search
from django.core.paginator import Paginator
def search_es(request):
return render(request,'search/search.html')
def results(request):
s = Search(using=Elasticsearch())
keyword = request.GET.get('q') # keyword that want to be found
print(keyword)
if keyword:
# posts = s.query('match_phrase_prefix',head_title=keyword)
# if posts.count() == 0:
posts = s.query(
"multi_match",
query=keyword,
fields=['head_title^5', 'description^5', 'description.ngram'],
# type="phrase_prefix",
)
posts = posts[0: 100]
else:
posts = ''
page = request.GET.get('page', 1)
paginator = Paginator(posts, 10)
try:
users = paginator.page(page)
except PageNotAnInteger:
users = paginator.page(1)
except EmptyPage:
users = paginator.page(paginator.num_pages)
context = {
'page_title': keyword,
'posts': users,
'count': posts.count(),
'keyword': keyword,
}
return render(request,'search/results.html',context)
results.html
{% if posts.has_other_pages %}
<ul class="pagination">
{% if posts.has_previous %}
<li>«</li>
{% else %}
<li class="disabled"><span>«</span></li>
{% endif %}
{% for i in posts.paginator.page_range %}
{% if posts.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% else %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}
{% if posts.has_next %}
<li>»</li>
{% else %}
<li class="disabled"><span>»</span></li>
{% endif %}
</ul>
{% endif %}
Hope for every possible solution.
Thank you very much.
ElasticSearch provided two parameters you can use for pagination: from and size. You can refer to https://elasticsearch-dsl.readthedocs.io/en/latest/search_dsl.html#pagination
for example:
posts = s[0:20].query(
"multi_match",
query=keyword,
fields=['head_title^5', 'description^5', 'description.ngram'],
# type="phrase_prefix",
)
to get first page, and page size is 20.

Where does the "page" GET parameter come from?

I am a beginner in Django, and I am really thoughtful about how things really work under the Django hood. I am currently implementing pagination in my web app.
Take a look at this view.py file:
def post_list(request):
object_list = Post.published.all(); # '.published' is a manager.
paginator = Paginator(object_list, 3); # 3 posts in each page.
page = request.GET.get("page");
try:
posts = paginator.page(page);
except PageNotAnInteger:
posts = paginator.page(1);
except EmptyPage:
posts = paginator.page(paginator.num_pages);
return render(request, "post/list.html", {"page": page, "posts": posts});
Isn't request.GET a dictionary object containing all the the GET request parameters that are in the url, and the .get() method used to return the value
for the given key inside the parameter? As my URL is currently just localhost:8000 when I start the application, why does it work if I pass the key "page"?
My list.html file:
{% extends "base.html" %}
{% block title %}My Blog{% endblock %}
{% block content %}
<h1>My Blog</h1>
{% for post in posts %}
<h2>{{ post.title }}</h2> <!-- How does this absurl work?-->
<p class="date">Published {{ post.publish }} by {{ post.author }}</p> <!-- What does '.publish' print?-->
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% include "pagination.html" with page=posts %}
<!-- The statement above is the little menu: "Page 1 of 2. Next" -->
<!-- It also sends the 'page' variable as a GET parameter. -->
{% endblock %}
My pagination.html file:
<!-- This pagination template expects a paginator object. -->
<div class="pagination">
<span class="step-links">
{% if page.has_previous %}
Previous
{% endif %}
<span class="current">
Page {{ page.number }} of {{ page.paginator.num_pages }}. <!-- what!? -->
</span>
{% if page.has_next %}
Next
{% endif %}
</span>
</div>
When there are no parameters in the request (when you hit http://localhost:8000 directly), the value of page will be None. That is the default behaviour of request.GET.get() when it can't find the key you're asking for - the same as a normal Python dictionary (because GET extends it).
# page will be None
page = request.GET.get("page")
This means that None is passed to paginator.page():
try:
# Passing None here
posts = paginator.page(page)
except PageNotAnInteger:
Which likely means (although we can't see the code of paginagor) that a PageNotAnInteger exception is raised, and thus a value of 1 is passed to paginagor.page():
try:
posts = paginator.page(page) # Raises PageNotAnInteger because None passed
except PageNotAnInteger:
# Posts are retrieved for page 1
posts = paginator.page(1)
The posts from the above call, and the value of page (still None) are then passed to the template.
return render(request, "post/list.html", {"page": page, "posts": posts});
The template list.html then iterates the posts and displays them.
Rather confusingly, when the pagination.html template is included, it defines a context variable called page to the current value of posts:
<!-- Pass the value of posts using a variable name of page -->
{% include "pagination.html" with page=posts %}
So the places where the pagination.html template refers to page, it is actually using the value of posts.
<!-- Really posts.number and posts.paginator.num_pages -->
Page {{ page.number }} of {{ page.paginator.num_pages }}
Hope that helps to explain things.
One other thing, you don't need to add a semi-colon to the end of every line in Python.

django paginator page numbers not working with if

I have the following template based pagination:
{% for ipage in transactions.paginator.page_range %}
<li {% if ipage == page %} class="active"{%endif%}>{{ipage}} - {{page}}</li>
{% endfor %}
The view page looks like this:
trans_list = Transaction.objects.all()
paginator = Paginator(trans_list, 15)
page = request.GET.get('page')
try:
transactions = paginator.page(page)
except PageNotAnInteger:
transactions = paginator.page(1)
except EmptyPage:
transactions = paginator.page(paginator.num_pages)
context = {
'page':page,
'transactions':transactions,
}
ipage and page both print the page number, but the if doesn't display the active class when they match in the for loop.
How can I get the if to match when the page number and the for loop index match?
That should do the trick.
{% for ipage in transactions.paginator.page_range %}
{% ifequal ipage transactions.number %}
<!-- Do something special for this page -->
{% else %}
<!-- All the other pages -->
{% endifequal %}
{% endfor %}

Django: each paginated page shows the same results

So I am trying to use pagination to display all the matching classes available on a certain day, but following the pagination docs, each page just returns the same 10 results. What am I missing/ what should I have in urlconf? Additionally, if I try using pagination to display search results, I get the error "The view search.views.search_classes didn't return an HttpResponse object" when I try to select the next page. Any input into either or both examples would be greatly appreciated.
#views.py
def upcoming_class_list(request, day):
try:
day = int(day)
except ValueError:
raise Http404()
today = datetime.date.today()
day_x = datetime.date.today() + datetime.timedelta(days=day)
day_x_classes = UpcomingClasses.objects.filter(class_date=day_x)
all_matches = day_x_classes
paginator = Paginator(all_matches, 10)
page = request.GET.get('page')
try:
matches = paginator.page(page)
except PageNotAnInteger:
matches = paginator.page(1)
except EmptyPage:
matches = paginator.page(paginator.num_pages)
return render_to_response('preview.html', {'today': today, 'tomorrow': tomorrow,
'past_classes': past_classes, 'day_x': day_x, 'day': day,
'day_x_classes': day_x_classes, 'yesterday': yesterday, 'matches': matches,
'page': page}, context_instance = RequestContext(request))
#urls.py
(r'^upcoming_class_list/plus/(\d{1,2})/$', upcoming_class_list),
#preview.html
<h3>Classes for {{ day_x }}</h3>
{% if matches %}
<div class="pagination">
<span class="step-links">
{% if matches.has_previous %}
« previous
{% endif %}
<span class="current">
Page {{ matches.number }} of {{ matches.paginator.num_pages }}
</span>
{% if matches.has_next %}
next »
{% endif %}
</span>
</div>
{% if day_x_classes %}
<ul type=none>
{% for class in day_x_classes %}
<li>
<ul type=none>
<li><strong>{{ class.type }}</strong></li>
<li>Teacher: {{ class.teacher }}</li>
<li>Class Description: {{ class.description }}</li>
...
</ul>
</li><br />
{% endfor %}
</ul>
{% endif %}
{% else %}
<p>There are currently no scheduled upcoming classes for {{ day_x }}.</p>
{% endif %}
Anything coming from GET or POST will be a string, so you're always hitting that first exception. Try:
try:
matches = paginator.page(int(page))
except (PageNotAnInteger, ValueError):
matches = paginator.page(1)
It's hard to guess at the rest of the issue without seeing the rest of your view. Looking at other bits in the view, you shouldn't need the check for day being an int as you've already assured that in your urls.py file with the regex, but you don't call the Http404 object, it's simply raise Http404
Ok so I figured out the answer to both of my questions. The first part was because I was making a dumb mistake in my template. The view urlconf were correct, but in my template, my for loop stated:
{% for class in day_x_classes %}
when I should have been using
{% for class in matches %}
since matches was being paginated, not day_x_classes.
As far as paginating my search results, I simply needed to edit the "previous" and "next" buttons from
« previous
from
« previous
to account for q (the searched term).
I hope that my mistakes will be able to help someone who was stuck in a similar situation.

Django pagination giving error: Caught TypeError while rendering: 'Page' object is not iterable

I am using django pagination, as told in documentation:
view part is :
def list(request):
job_list = Job.objects.all()
paginator = Paginator(job_list, 25) # Show 25 jobs per page
page = request.GET.get('page',1)
try:
jobs = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
jobs = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
jobs = paginator.page(paginator.num_pages)
return render_to_response('jobs/list.html', {"jobs": jobs})
and template is:
<div>
{% for job in jobs %}
{# Each "contact" is a Contact model object. #}
{{ job.title|upper }}<br />
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if contacts.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ contacts.number }} of {{ contacts.paginator.num_pages }}.
</span>
{% if contacts.has_next %}
next
{% endif %}
</span>
</div>
</div>
But it gives the error saying:
In template d:\programming\django_projects\kaasib\templates\jobs\list.html, error at line 32
Caught TypeError while rendering: 'Page' object is not iterable
I am new in django and this error seems general but very strange. Because in loop there is some other variable, not job. So please tell if anyone have any idea about it.
thanks
The error should be clear - the variable you've called jobs actually contains a Page object from the paginator. Which is as it should be, as you assigned jobs to paginator.page(x). So obviously, it contains a Page.
The documentation shows what to do:
{% for job in jobs.object_list %}
etc.