Creating django pagination using generic view - django

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

Related

django list_filter in html

How do i import this list_filter into my html? do you have any documentation that easy to follow and easy to understand? is it possible?
class ArticleListView(ListView):
model = StudentsEnrollmentRecord
s=StudentsEnrollmentRecord.objects.all()
paginate_by = 50 # if pagination is desired
searchable_fields = ["Student_Users", "id", "Section"]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['now'] = timezone.now()
return context
You can use template tags to loop your object and populate your html:
https://docs.djangoproject.com/en/3.0/ref/templates/builtins/
To give you a kickstart:
<ul>
{% for filter in s %}
<li>{{ filter.Student_Users }}</li>
{% endfor %}
</ul>
This will basically create one <li> per each item in your queryset.
Make sure that the required data is returned properly from your view via the context:
context = {
'queryset': s,
}
return (context)

Django taggit related post not showing on template

Recently I installed django-taggit and included in the post model. I did the views and urls files and no error. On the home page I get a few posts and 10 tags related to posts. When I click on one of the tags it goes to the right slug but the page is blank. What am I missing here? Any help would be appreciated.
This is my code, views.py, urls.py, home.html
def home(request):
tutorial_list = Post.objects.all().order_by('-id')[:3]
context = {
'tutorial_list': tutorial_list,
}
return render(request, "home.html", context)
class TagMixin(object):
def get_context_data(self, **kwargs):
context = super(TagMixin, self).get_context_data(**kwargs)
context['tags'] = Tag.objects.all()
return context
class TagIndexView(TagMixin, ListView):
template_name = 'home.html'
model = Post
context_object_name = 'post'
def get_queryset(self):
return Post.objects.filter(tags__slug=self.kwargs.get('slug'))
'''urls.py'''
path('tag/<slug:slug>/', views.TagIndexView.as_view(), name='tagged'),
'''home.html'''
{% for tutorial in tutorial_list %}
{{ tutorial.title }}
{% endfor %}
{% for tag in tags %}
<li>{{ tag.name }}</li>
{% endfor %}
The context_object_name of TagIndexView does not match the variable name you're iterating over in home.html - the template is using tutorial_list so context_object_name should be 'tutorial_list' instead of 'post'.
class TagIndexView(TagMixin, ListView):
template_name = 'home.html'
model = Post
context_object_name = 'tutorial_list'
def get_queryset(self):
return Post.objects.filter(tags__slug=self.kwargs.get('slug'))

Is it possible to render 2 queryset in ListView?

I am new to django and recently read a tutural that describes the usage of class based view. But when I try to render 2 different queryset (all data and filterd data) to the same template, I can't find the solution for display 2 different queryset. Can anyone suggest the best solution about this?
I know writing the function based view is much easy for this, but I hope it can be done by Class based view, thank you
#in view.py
from django.views.generic import ListView
from books.models import Book
class BookListView(generic.ListView):
model = Book
context_object_name = 'my_book_list'
queryset = Book.objects.all()
template_name = 'books/my_arbitrary_template_name_list.html'
# queryset = Book.objects.filter(title='war')?
#in templage.py
#Main content
<div class="main_content">
<h1>All War</h1>
<ul>
{% for book in book_list %}
<li>
{{book.title}}
</li>
{% for endfor %}
</ul>
</div>
#Sidebar
<div class="sidebar">
<h1>All War</h1>
<ul>
{% for book in book_list %}
<li>
{{book.title}}
</li>
{% for endfor %}
</ul>
</div>
You should look into get_context_data to add additional queries you want.
The view might look something like this
class BookListView(generic.ListView):
model = Book
context_object_name = 'my_book_list'
queryset = Book.objects.all()
template_name = 'books/my_arbitrary_template_name_list.html'
def get_context_data(self, **kwargs):
context = super(BookListView, self).get_context_data(**kwargs)
context['second_queryset'] = # YOUR QUERY HERE
return context
For this example the second query is second_queryset and it can be accessed in the html template as {{second_queryset}}. You can add more if you did the same for more queries.
def get_context_data(self, **kwargs):
context = super(BookListView, self).get_context_data(**kwargs)
context['second_queryset'] = # YOUR QUERY HERE
context['third_queryset'] = # YOUR QUERY HERE
...
return context
You can learn about it more in here.

Reversing results for current page (django paginator generic view)

I'm using the standard django paginator in my generic view like this:
def get_context_data(self, **kwargs):
context = super(ArchivePagedView, self).get_context_data(**kwargs)
article_list = Article.published
#=====================================
paginator = Paginator(article_list, self.paginate_by)
page = self.request.GET.get('page')
try:
article_list = paginator.page(page)
except PageNotAnInteger:
article_list = paginator.page(1)
except EmptyPage:
article_list = paginator.page(paginator.num_pages)
if 'reverse' in self.request.GET:
article_list = article_list.reverse() #This doesn't work!
else:
article_list = article_list.all()
context['article_list'] = article_list
return context
As you can see I want to override article_list with the same list, but in reversed direction, if reverse is in the URL in behind the question mark. That information I get by 'reverse' in self.request.GET.
But I get an error: AttributeError: 'Page' object has no attribute 'reverse'. How do I reverse this? (I don't want to have duplicated code in my template.)
Before I fixed this by making an extra context variable (context['reverse']) which says whether the list should be reversed or not, and then I used duplicated code like this:
{% if reverse %}
{% for article in article_list reversed %}
... some code
{% endfor %}
{% else %}
{% for article in article_list %}
... the same code
{% endfor %}
{% endif %}
I wonder if there was no better solution.
Try this
article_list.object_list = list(reversed(article_list.object_list))

Pagination in Wagtail

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.)