diff options
author | lxf <sng@luxagraf.net> | 2022-05-14 16:38:07 -0400 |
---|---|---|
committer | lxf <sng@luxagraf.net> | 2022-05-14 16:38:07 -0400 |
commit | bb3973ffb714c932e9ec6dd6a751228dc71fe1d3 (patch) | |
tree | 6fa32f9392ad2ec32271349b86a4c1388fd6ba95 /app/utils/next_prev.py |
initial commit
Diffstat (limited to 'app/utils/next_prev.py')
-rw-r--r-- | app/utils/next_prev.py | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/app/utils/next_prev.py b/app/utils/next_prev.py new file mode 100644 index 0000000..766add1 --- /dev/null +++ b/app/utils/next_prev.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +# from https://github.com/gregplaysguitar/django-next-prev/blob/master/next_prev.py + +from functools import partial + +from django.db import models + +if not locals().get('reduce'): + from functools import reduce + +__version__ = '1.0.1' +VERSION = tuple(map(int, __version__.split('.'))) + + +def get_model_attr(instance, attr): + """Example usage: get_model_attr(instance, 'category__slug')""" + for field in attr.split('__'): + instance = getattr(instance, field) + return instance + + +def next_or_prev_in_order(instance, qs=None, prev=False, loop=False): + """Get the next (or previous with prev=True) item for instance, from the + given queryset (which is assumed to contain instance) respecting + queryset ordering. If loop is True, return the first/last item when the + end/start is reached. """ + + if not qs: + qs = instance.__class__.objects.all() + + if prev: + qs = qs.reverse() + lookup = 'lt' + else: + lookup = 'gt' + + q_list = [] + prev_fields = [] + + if qs.query.extra_order_by: + ordering = qs.query.extra_order_by + elif qs.query.order_by: + ordering = qs.query.order_by + elif qs.query.get_meta().ordering: + ordering = qs.query.get_meta().ordering + else: + ordering = [] + + ordering = list(ordering) + + # if the ordering doesn't contain pk, append it and reorder the queryset + # to ensure consistency + if 'pk' not in ordering and '-pk' not in ordering: + ordering.append('pk') + qs = qs.order_by(*ordering) + + for field in ordering: + if field[0] == '-': + this_lookup = (lookup == 'gt' and 'lt' or 'gt') + field = field[1:] + else: + this_lookup = lookup + q_kwargs = dict([(f, get_model_attr(instance, f)) + for f in prev_fields]) + key = "%s__%s" % (field, this_lookup) + q_kwargs[key] = get_model_attr(instance, field) + q_list.append(models.Q(**q_kwargs)) + prev_fields.append(field) + try: + return qs.filter(reduce(models.Q.__or__, q_list))[0] + except IndexError: + length = qs.count() + if loop and length > 1: + # queryset is reversed above if prev + return qs[0] + return None + + +next_in_order = partial(next_or_prev_in_order, prev=False) +prev_in_order = partial(next_or_prev_in_order, prev=True) |