I have a page based on a model object, and I want to have links to the previous and next pages. I don't like my current solution because it requires evaluating the entire queryset (to get the ids
list), and then two more get
queries. Surely there is some way this can be done in one pass?
def get_prev_and_next_page(current_page):
ids = list(Page.objects.values_list("id", flat=True))
current_idx = ids.index(current_page.id)
prev_page = Page.objects.get(id=ids[current_idx-1])
try:
next_page = Page.objects.get(id=ids[current_idx+1])
except IndexError:
next_page = Page.objects.get(id=ids[0])
return (prev_page, next_page)
Sort order is defined in the model, so doesn't have to be handled here, but note that you can't assume that ids are sequential.
Sounds like something the Paginator set to a threshold of 1 would do well at.
# Full query set...
pages = Page.objects.filter(column=somevalue)
p = Paginator(pages, 1)
# Where next page would be something like...
if p.has_next():
p.page(p.number+1)
Documentation here and here.
I'm new to Python and Django, so maybe my code is not optimal, but check this out:
def get_prev_and_next_items(target, items):
''' To get previous and next objects from QuerySet '''
found = False
prev = None
next = None
for item in items:
if found:
next = item
break
if item.id == target.id:
found = True
continue
prev = item
return (prev, next)
And in view something like that:
def organisation(request, organisation_id):
organisation = Organisation.objects.get(id=organisation_id)
...
prev, next = get_prev_and_next_items(organisation, Organisation.objects.all().order_by('type'))
...
return render_to_response('reference/organisation/organisation.html', {
'organisation': organisation,
'prev': prev,
'next': next,
})
Definitely not optimal for «heavy» querysets, but in most cases works like a charm. :)
Check out django-next-prev, I wrote it to solve this exact problem. In this case:
from next_prev import next_in_order, prev_in_order
def get_prev_and_next_page(current_page):
page_qs = Page.objects.all() # this could be filtered, or ordered
prev_page = prev_in_order(current_page, page_qs)
next_page = next_in_order(current_page, page_qs)
return (prev_page, next_page)