-
-
Save dokterbob/1004216 to your computer and use it in GitHub Desktop.
from django import template | |
register = template.Library() | |
from templatetag_sugar.register import tag | |
from templatetag_sugar.parser import Constant, Variable, Name | |
from .utils import get_next_or_previous | |
""" | |
Efficient and generic get next/previous tags for the Django template language, | |
using Alex Gaynor's excellent templatetag_sugar library. | |
The library can be found at: http://pypi.python.org/pypi/django-templatetag-sugar | |
Usage: | |
{% load next_previous %} | |
... | |
{% get_next in <queryset> after <object> as <next> %} | |
{% get_previous in <queryset> before <object> as <previous> %} | |
""" | |
@tag(register, [Constant("in"), Variable(), Constant("after"), Variable(), Constant("as"), Name()]) | |
def get_next(context, queryset, item, asvar): | |
context[asvar] = get_next_or_previous(queryset, item, next=True) | |
return "" | |
@tag(register, [Constant("in"), Variable(), Constant("before"), Variable(), Constant("as"), Name()]) | |
def get_previous(context, queryset, item, asvar): | |
context[asvar] = get_next_or_previous(queryset, item, next=False) | |
return "" |
from django.db.models import Q | |
from django.db.models.sql.query import get_order_dir | |
def get_next_or_previous(qs, item, next=True): | |
""" | |
Get the next or previous object in the queryset, with regards to the | |
item specified. | |
""" | |
# If we want the previous object, reverse the default ordering | |
if next: | |
default_ordering = 'ASC' | |
else: | |
default_ordering = 'DESC' | |
# First, determine the ordering. This code is from get_ordering() in | |
# django.db.sql.compiler | |
if qs.query.extra_order_by: | |
ordering = qs.query.extra_order_by | |
elif not qs.query.default_ordering: | |
ordering = qs.query.order_by | |
else: | |
ordering = qs.query.order_by or qs.query.model._meta.ordering | |
assert not ordering == '?', 'This makes no sense for random ordering.' | |
query_filter = None | |
for field in ordering: | |
item_value = getattr(item, field) | |
# Account for possible reverse ordering | |
field, direction = get_order_dir(field, default_ordering) | |
# Either make sure we filter increased values or lesser values | |
# depending on the sort order | |
if direction == 'ASC': | |
filter_dict = {'%s__gt' % field: item_value} | |
else: | |
filter_dict = {'%s__lt' % field: item_value} | |
# Make sure we nicely or the conditions for the queryset | |
if query_filter: | |
query_filter = query_filter | Q(**filter_dict) | |
else: | |
query_filter = Q(**filter_dict) | |
# Reverse the order if we're looking for previous items | |
if default_ordering == 'DESC': | |
qs = qs.reverse() | |
# Filter the queryset | |
qs = qs.filter(query_filter) | |
# Return either the next/previous item or None if not existent | |
try: | |
return qs[0] | |
except IndexError: | |
return None |
Dude, you saved my life.
I wrote a similar tool, partly inspired by this, which isn't tied to the django template engine, so can be used in any context: https://github.com/gregplaysguitar/django-next-prev
I'm having trouble getting related field using this code. If I have a model:
class UserSkill():
user = foreignKey(User)
....
so, if I filter: userskill__user__email
I get an error.
How can I solve this?
Thanks
I moved to https://github.com/gregplaysguitar/django-next-prev a so much better solution since it provides customized order_by
Thanks @gregplaysguitar
I couldn't figure this out exactly with my setup, so I wrote a similar one using filter. It's very short because I just needed a previous/next button on an object's detail page. https://gist.github.com/clhefton/a3535e64a9b314339a2e1aabdfb4d1c7
This will not work correctly for more than 1 order field. You have to split comparison like this:
(1field__gt=val) | (1field=val & 2field__gt=val) ...