Pagination in Wagtail - django

I'm fairly new to Wagtail, and I am in the process of creating a site that will have a Resources (blog) section and I'm not sure how to implement pagination so that there are only 5 posts on each page and the user has to click a number (1, 2, 3, etc.) to go to the next page to see the next 5 posts.
I have this in my template for the pagination section of the resource/blog index page:
<ul class="pagination">
<li><i class="fa fa-angle-left"></i></li>
<li class="active">1</li>
<li>2</li>
<li>3</li>
<li><i class="fa fa-angle-right"></i></li>
</ul>
What code do I need to incorporate to make this functional? Thanks in advance.

Django provides the module django.core.paginator for this purpose: https://docs.djangoproject.com/en/1.10/topics/pagination/ . Using this within Wagtail is very similar to the examples in the Django documentation - the only real difference is that when you're setting up the Paginator object to be passed to the template, you do that with a get_context method on the page model, instead of a view function. Your model definition will look something like this:
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
class ResourceIndexPage(Page):
# ...
def get_context(self, request):
context = super(ResourceIndexPage, self).get_context(request)
# Get the full unpaginated listing of resource pages as a queryset -
# replace this with your own query as appropriate
all_resources = ResourcePage.objects.live()
paginator = Paginator(all_resources, 5) # Show 5 resources per page
page = request.GET.get('page')
try:
resources = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
resources = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
resources = paginator.page(paginator.num_pages)
# make the variable 'resources' available on the template
context['resources'] = resources
return context
Within your template, you can now loop over the items using {% for resource in resources %}, and display the pagination links as follows:
<ul class="pagination">
{% if resources.has_previous %}
<li><i class="fa fa-angle-left"></i></li>
{% endif %}
{% for page_num in resources.paginator.page_range %}
<li {% if page_num == resources.number %}class="active"{% endif %}>{{ page_num }}</li>
{% endfor %}
{% if resources.has_next %}
<li><i class="fa fa-angle-right"></i></li>
{% endif %}
</ul>

I very much appreciate that you got me here - thanks so much for the assist. I had to make some adjustments to make it work. Here's the model if anyone comes across the same issue:
class NewsIndexPage(Page):
intro = RichTextField(blank=True)
def get_context(self, request):
context = super(NewsIndexPage, self).get_context(request)
# Get the full unpaginated listing of resource pages as a queryset -
# replace this with your own query as appropriate
blogpages = self.get_children().live().order_by('-first_published_at')
paginator = Paginator(blogpages, 3) # Show 3 resources per page
page = request.GET.get('page')
try:
blogpages = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
blogpages = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
blogpages = paginator.page(paginator.num_pages)
# make the variable 'resources' available on the template
context['blogpages'] = blogpages
return context
...and here's the HTML:
<ul class="pagination">
{% if blogpages.has_previous %}
<li>
<i class="fa fa-angle-left"></i>
</li>
{% endif %}
{% for page_num in blogpages.paginator.page_range %}
<li {% if page_num == blogpages.number %} class="active"{% endif %}>
{{ page_num }}
</li>
{% endfor %}
{% if resources.has_next %}
<li>
<i class="fa fa-angle-right"></i>
</li>
{% endif %}
</ul>
It works like a charm - and adds to the learning curve!

In case it's useful to anyone, I wanted this to work as closely as possible to the class-based view ListView, and so I ended up with this:
from django.core.paginator import Paginator, InvalidPage
from django.http import Http404
from django.utils.translation import gettext as _
from wagtail.core.models import Page
class ArticleListPage(Page):
# Some Page variables set here. #
# Pagination variables:
paginator_class = Paginator
paginate_by = 10
page_kwarg = 'page'
paginate_orphans = 0
allow_empty = False
def get_context(self, request):
context = super().get_context(request)
queryset = Page.objects.live()
paginator, page, queryset, is_paginated = self.paginate_queryset(
queryset, self.paginate_by, request)
context.update({
'paginator': paginator,
'page_obj': page,
'is_paginated': is_paginated,
'object_list': queryset,
})
return context
def paginate_queryset(self, queryset, page_size, request):
"""
Adapted from the ListView class-based view.
Added the request argument.
"""
paginator = self.paginator_class(
queryset,
self.paginate_by,
orphans=self.paginate_orphans,
allow_empty_first_page=self.allow_empty)
page_kwarg = self.page_kwarg
page = request.GET.get(page_kwarg) or 1
try:
page_number = int(page)
except ValueError:
if page == 'last':
page_number = paginator.num_pages
else:
raise Http404(_("Page is not 'last', nor can it be converted to an int."))
try:
page = paginator.page(page_number)
return (paginator, page, page.object_list, page.has_other_pages())
except InvalidPage as e:
raise Http404(_('Invalid page (%(page_number)s): %(message)s') % {
'page_number': page_number,
'message': str(e)
})
This will give you the same paginator, page_obj, is_paginated and object_list variables in your template that you would get with a normal Django ListView.
(Using python 3, Django 2.1 and Wagtail 2.3.)

Related

How can I Render Data To Main Template In Other Extended View From Main Template?

First of all, hello everyone. I apologize to everyone for my bad English, I will try my best. I am making a video site, in this site there are three main views, they are single-video-view, index-view and main-view. All other views extends from main-view (%extends 'base/main.html' %). There is a sidebar that should appear on all pages. In this sidebar, the categories of pre-loaded videos should be listed.
My urls.py:
urlpatterns = [
path('',views.index,name="index"),
path('main/',views.main,name="main"),
path('video/<slug:slug>/',views.video,name="video"),
#path('test/',views.category,name="setcategory"),
path('apivideo/',views.apivideo,name="apivideo"),
]
If I visit main.html directly working fine
Single video view, categories not listing
Index view, categories not listing
View.py:
def index(request):
# q = request.GET.get('q')
# if request.GET.get('q') != None:
if 'q' in request.GET and request.GET['q']:
page = request.GET.get('page', 1)
q = request.GET['q']
videos = Video.objects.filter(
Q(video_title__icontains=q) | Q(video_tags__icontains=q))
paginator = Paginator(videos, 24)
videos = paginator.page(page)
return render(request, 'base/index.html', {'videos': videos, 'q': q})
else:
main(request)
videos = Video.objects.all().order_by("-id")
paginator = Paginator(videos, 24)
page_number = request.GET.get('page')
videos = paginator.get_page(page_number)
return render(request, 'base/index.html', {'videos': videos})
def main(request):
setcategory = []
category = Video.objects.values_list('video_tags', flat=True)
for i in range(0, len(category)):
for x in range(0, len(category[i])):
categorycount = Video.objects.filter(Q(video_title__icontains=str(
category[i][x]).lower()) | Q(video_tags__icontains=str(category[i][x]).lower())).count()
if (categorycount >= 10):
print("if: " + str(categorycount))
setcategory.append(str(category[i][x]).lower())
else:
print("else: " + str(categorycount))
break
setcategory = sorted([*set(setcategory)])
return render(request, 'base/main.html', {'setcategory': setcategory})
def video(request, slug):
eachvideo = Video.objects.get(video_slug=slug)
randomVideo = list(Video.objects.all())
randomVideo = random.sample(randomVideo, 20)
return render(request, 'base/video.html', {'eachvideo': eachvideo, 'randomVideo': randomVideo})
Main.html:
Some HTML..
<ul class="sidebar navbar-nav toggled">
{% if setcategory %}
{% for category in setcategory %}
<li class="nav-item active">
<a class="nav-link" href="{% url 'index' %}?q={{category}}">
<i class="fas fa-fw fa-home"></i>
<span>{{category|title}}</span>
</a>
</li>
{% endfor %}
{% endif %}
</ul>
Some HTML..
The code I wrote for this works fine, but it only works when I add a redirect to the main-view specifically in the url .py file and go to the main view page. What I want is for these categories to appear in the sidebar when the user browses the index or other pages.

Pagination Based on Queryset

I'm trying to implement pagination on my Django app that is based on the filtered queries, but the pagination shows all the objects even the ones not filtered, any insight on what I'm doing wrong?
Any assistance would be appreciated.
def searchPropertyListView(request):
city = City.objects.all().annotate(
num_property=Count("property")).order_by("-num_property")
categories = Category.objects.all()
purposes = Purpose.objects.all()
featured = list(Property.objects.filter(featured=True))
shuffle(featured)
querySet = Property.objects.all()
city_or_neighborhood = request.GET.get('city_or_neighborhood')
category = request.GET.get('category')
purpose = request.GET.get('purpose')
if city_or_neighborhood != '' and city_or_neighborhood is not None:
querySet = querySet.filter(Q(city__title__icontains=city_or_neighborhood)
| Q(neighborhood__title__icontains=city_or_neighborhood)
).distinct()
if category != '' and category is not None:
querySet = querySet.filter(category__title=category)
if purpose != '' and purpose is not None:
querySet = querySet.filter(purpose__title=purpose)
paginator = Paginator(querySet, 1)
page = request.GET.get('page')
try:
querySet = paginator.page(page)
except PageNotAnInteger:
querySet = paginator.page(1)
except EmptyPage:
querySet = paginator.page(paginator.num_pages)
Paginator.get_page is New since Django 2.0.
def searchPropertyListView(request):
...
Your code here
...
paginator = Paginator(querySet, 1)
page = request.GET.get('page')
page_obj = paginator.get_page(page)
And html
{% for property in page_obj %}
{{ property.city.title }}<br>
...
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
« first
previous
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
last »
{% endif %}
</span>
</div>
See Using Paginator in a view function

How to update template on Django view request post

I am doing some filtering. I have initial results on my page, then there is a form, that on select, I want to filter some data and update template.
The template doesn't seem to be updated though.
from .services import service
# Home Page View
def home_view(request, *args, **kwargs):
update_list = service.get_updates()
if request.method == 'POST':
filter_string = request.POST.get('filter')
update_list = [update for update in update_list if update['type'] == filter_string]
print(len(update_list))
page = request.GET.get('page', 1)
paginator = Paginator(update_list, 2)
try:
updates = paginator.page(page)
except PageNotAnInteger:
updates = paginator.page(1)
except EmptyPage:
updates = paginator.page(paginator.num_pages)
context = {
'updates': updates
}
return render(request, "home.html", context)
Template file:
<form method="post" id="filterformhome">
{% csrf_token %}
<select class="frm-input" onchange="homeFilter(this);">
<option value="job">{% trans "Jobs" %}</option>
<option value="market">{% trans "Products" %}</option>
<option value="question">{% trans "Questions" %}</option>
<option value="blog">{% trans "Blogs" %}</option>
</select>
</form>
{% for update in updates %}
{{update.title}}
{% endfor %}
What would be the better way of doing this?
You need to use JS for that, otherwise you will only see the change reflected by refreshing the page.

Creating django pagination using generic view

I am facing a slight problem in django pagination using generic views, passing multiple models to a template with one of the models that has to paginated. The pager does not show next pages on my template,but produces just a single page.
Below is my code :
views.py:
class homeView(generic.ListView):
template_name = 'success/home_page.html'
context_object_name="articles"
paginate_by = 3
def get_queryset(self):
articles =Articles.objects.order_by('article_title')
paginator = Paginator(articles,self.paginate_by)
page = self.request.GET.get('page')
try:
articles = paginator.page(page)
except PageNotAnInteger:
articles = paginator.page(1)
except EmptyPage:
articles = paginator.page(paginator.num_pages)
return articles
def get_context_data(self,**kwargs):
context = super(homeView, self).get_context_data(**kwargs)
context['quote'] = Quotes.objects.order_by('quote')
return context
Template:
{% for article in articles %}
{{ article.article_title}}
{{ article.aticle_hit}}
{% endfor % %}
<div class="pagination">
{% if articles.has_previous %}
<a class="navlink" href="?page={{articles.previous_page_number}}">Prev</a>
{% endif %}
Page {{articles.number}} of {{articles.paginator.num_pages}}
{% if articles.has_next %}
<a class="navlink" href="?page={{articles.next_page_number}}">next</a>
{% endif %}
</div>
Just page one of my paginated page shows. Next and previous do not work at all. Please help me with a better way to do pagination using generic view and passing multiple models in to a single template.
As said, trim down. If you get an error, post it, that will help.
class homeView(generic.ListView):
template_name = 'success/home_page.html'
context_object_name = "articles"
paginate_by = 3
model = Articles
def get_context_data(self, **kwargs):
context = super(homeView, self).get_context_data(**kwargs)
context['quote'] = Quotes.objects.order_by('quote')
return context

Django Paginator Error

I'm been trying to implement django paginator into my whiteboard app so I can split the pictures into different pages.
The problem occurs when I attempt to move across different pages.I limited each page to 1 objects and uploaded few pictures to test if the pagination works between pages but when I try to move across different pages using the pagination method, it doesn't respond.
http://img854.imageshack.us/img854/3303/94627386.jpg
I'm been researching and testing for solutions to this problems through the django pagination docs and I think problem lay at the pagination module method at my template.
My views.py
def Boat(request ,animal_id):
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('world:LoginRequest'))
picture = Picture.objects.filter(board=animal_id)
paginator = Paginator(picture,1)
page = request.GET.get('page')
try:
picture = paginator.page(page)
except PageNotAnInteger:
picture = paginator.page(1)
picture = paginator.page(paginator.num_pages)
return render(request,'boat.html',{'picture':picture })
My boat.html
{% if picture.object_list %}
<ul>
{% for pet in picture.object_list %}
{% if pet.image %}
<br>
<img src= "{{ pet.image.url }}" </a>
<br>
</a>
</li>
{% endif %}
<br>
View Comment Like<br/>
{% for c in picture %}
{% ifequal c.picture.id pet.id %}
<br>{{ c.body }}</li>
<br>{{ c.created}}</li>
<br>{{ c.user}}</li>
{% endifequal %}
% endfor %}
{% endfor %}
</ul>
{% endif %}
Add Pictures to your board<br/>
{% if number %}
{{number}}
{% endif %}
Return back to Profile<br/>
<br><br><br><br><br>
<div class="pagination">
<span class="step-links">
{% if picture.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ picture.number }} of {{ picture.paginator.num_pages }}.
</span>
{% if picture.has_next %}
next
{% endif %}
</span>
</div>
Parts of my module
class Picture(models.Model):
user = models.ForeignKey(User)
board = models.ForeignKey(Board,blank=False,null=False,related_name='board')
image = models.FileField(upload_to="images/",blank=True)
description = models.TextField()
is_primary = models.BooleanField(default=False)
def __unicode__(self):
return self.description
def Boat(request ,animal_id):
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('world:LoginRequest'))
picture = Picture.objects.filter(board=animal_id)
paginator = Paginator(picture,1)
try:
page = int(request.GET.get('page', '1'))
except ValueError:
page = 1
try:
picture = paginator.page(page)
except (EmptyPage, InvalidPage):
picture = paginator.page(paginator.num_pages)
picture = paginator.page(paginator.num_pages)
return render(request,'boat.html',{'picture':picture })
#this is view
#this is views files
#login_required(login_url='/login')
# class Base_page_list2(ListView):
def Base_page_list(request,*args, **kwargs):
# tiket = Tiket.objects.all()
# lastest_tiket =Tiket.objects.order_by('-id').all()[:8]
tiket_list = Tiket.objects.all()
paginator = Paginator(tiket_list,2)
page = request.GET.get('page')
page_obj = paginator.get_page(page)
context ={
'tiket':None,
'page_obj':page_obj,
}
context['tiket']=['page_obj']
if request.user.is_superuser:
context['tiket']= Tiket.objects.all()
elif not request.user.is_hrm:
raise Http404('شما نمی توانید به این صحفه دسترسی داشته باشید')
elif request.user.is_mis :
context['tiket']= Tiket.objects.filter(status_tag='s')
elif request.user.is_mali:
context['tiket']=Tiket.objects.filter(status_tag='m')
elif request.user.is_mosh:
context['tiket']=Tiket.objects.filter(status_tag='c')
elif request.user.is_modir:
context['tiket']=Tiket.objects.filter(status_tag='b')
elif request.user.is_kz:
context['tiket']=Tiket.objects.filter(status_tag='k')
elif request.user.is_pa:
context['tiket']=Tiket.objects.filter(status_tag='p')
else:
context['page_obj']['tiket']=Tiket.objects.filter(author=request.user)
return render(request,'hrm_account/base.html',context)
`enter code here`error
File "/home/ali/Desktop/testsharen2/sharen/sharen_hrm/views.py", line 128, in Base_page_list
context['page_obj']['tiket']=Tiket.objects.filter(author=request.user)
TypeError: 'Page' object does not support item assignment