varnie
varnie

Reputation: 2595

Django pagination dependent on ordering

I have the following model (simplified):

class Post(models.Model):
    title = models.CharField(max_length=20)
    date_created = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ('-date_created',) # note the "reverse" ordering here

, then my custom DetailView based on generic.DetailView:

class PostDetailView(generic.DetailView):
    queryset = Post.objects.all()

and, finally, the following template:

{% with object.get_previous_by_date_created as prev %}
    {% if prev %}
    <a class="nav-link-prev"
       href="{% url "blog:post_detail" pk=prev.id %}">Previous</a>
    {% endif %}
{% endwith %}

{% with object.get_next_by_date_created as next %}
    {% if next %}
    <a class="nav-link-next"
       href="{% url "blog:post_detail" pk=next.id %}">Next</a>
    {% endif %}
{% endwith %}

My pagination implemented this way works, but due to the reverse ordering (i.e -date_created) field, its Previous/Next labels are misplaced. In other words my template outputs "Previous" where it should be "Next", and vice versa. If I remove a - sign in ordering = ('-date_created',) it will work fine, but that's not the ordering I would like to have on my website.

Is there an easy and idiomatic way of fixing this misbehavior? What am I missing?

Upvotes: 1

Views: 147

Answers (3)

Manan M.
Manan M.

Reputation: 1394

I think you have to use DetailView with mixin MultipleObjectMixin like below...

from django.views.generic.detail import DetailView
from django.views.generic.list import MultipleObjectMixin
from django.core import paginator

class PostDetailView(DetailView, MultipleObjectMixin):
    model = Post
    paginate_by = 5

    def get_context_data(self, **kwargs):
        object_list = Post.objects.all().order_by('-date_created')
        context = super(PostDetailView, self).get_context_data(object_list=object_list, **kwargs)
        post_paginator = paginator.Paginator(object_list, self.paginate_by)
        # Catch invalid page numbers
        try:
            post_page_obj = post_paginator.page(purchases_page)
        except (paginator.PageNotAnInteger, paginator.EmptyPage):
            post_page_obj = post_paginator.page(1)

        context["post_page_obj"] = post_page_obj
        return context

Now, you should be able to use pagination method with post_page_obj variable

Upvotes: -1

varnie
varnie

Reputation: 2595

I went this route and was able to solve the issue:

class PostDetailView(generic.DetailView):
    model = Post

    def get_context_data(self, **kwargs):
        context = super(PostDetailView, self).get_context_data(**kwargs)

        next = Post.objects.filter(pk__lt=self.object.pk).order_by('-pk')[:1]
        prev = Post.objects.filter(pk__gt=self.object.pk).order_by('pk')[:1]
        if prev:
            context['prev'] = prev[0]
        if next:
            context['next'] = next[0]

        return context

now in template.html:

{% if prev %}
<a class="nav-link-prev"
   href="{% url "post_detail" pk=prev.id %}">Previous
</a>
{% endif %}

{% if next %}
    <a class="nav-link-next"
       href="{% url "post_detail" pk=next.id %}">Next
    </a>
{% endif %}

What do you think of this approach?

Upvotes: 0

Horatiu Jeflea
Horatiu Jeflea

Reputation: 7404

Add

ordering = ['-date_created']

or

ordering = Post._meta.ordering

to PostListView

Upvotes: 0

Related Questions