eaman
eaman

Reputation: 1139

Next previous links from a query set / generic views

I have a quite simple query set and a related generic views:

f_detail = {
         'queryset': Foto.objects.all(),
         'template_name': 'foto_dettaglio.html',
         "template_object_name" : "foto",
       }

urlpatterns = patterns('',
# This very include
(r'^foto/(?P<object_id>\d+)/$', list_detail.object_detail, f_detail, ),
)

Just a template for generating a detail page of a photo: so there's no view.


Is there an easy way to have a link to previous | next element in the template without manualy coding a view ?

Somthing like a:

{% if foto.next_item %} 
  <a href="/path/foto/{{ foto.next_item.id_field }}/">Next</a> 
{% endif}

Upvotes: 15

Views: 5276

Answers (3)

Brandon Henry
Brandon Henry

Reputation: 3720

class Foto(model):
  ...
  def get_next(self):
    next = Foto.objects.filter(id__gt=self.id)
    if next:
      return next.first()
    return False

  def get_prev(self):
    prev = Foto.objects.filter(id__lt=self.id).order_by('-id')
    if prev:
      return prev.first()
    return False

you can tweak these to your liking. i just looked at your question again... to make it easier than having the if statement, you could make the methods return the markup for the link to the next/prev if there is one, otherwise return nothing. then you'd just do foto.get_next etc. also remember that querysets are lazy so you're not actually getting tons of items in next/prev.

Upvotes: 24

Tom Aldcroft
Tom Aldcroft

Reputation: 2542

The Foto version above has a couple of shortcomings:

  • Doing a boolean evaluation like if next: can be slow since it basically loads the entire QuerySet result. Use next.exists() or the try/except like in my version.
  • The get_prev() result is wrong because you need to reverse the ordering in this case.

So FWIW here is my version, which is for a generic primary key:

def get_next(self):
    """
    Get the next object by primary key order
    """
    next = self.__class__.objects.filter(pk__gt=self.pk)
    try:
        return next[0]
    except IndexError:
        return False

def get_prev(self):
    """
    Get the previous object by primary key order
    """
    prev = self.__class__.objects.filter(pk__lt=self.pk).order_by('-pk')
    try:
        return prev[0]
    except IndexError:
        return False

Upvotes: 7

Andrew Swihart
Andrew Swihart

Reputation: 619

If you'll accept Model.objects.all() as your queryset, and you are ok with grabbing next / previous items by a date field (usually a 'created' field with auto_now_add=True will give the same order as object id's), you can use get_next_by_foo() and get_previous_by_foo(), where 'foo' is the date field.

For next / previous links from a more complicated QuerySet, using the Paginator with threshold set to one seems like it might be the best option.

Upvotes: 1

Related Questions