Метод реализован в файле django/db/models/base.py
[GitHub] :
def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs):
if not self.pk:
raise ValueError("get_next/get_previous cannot be used on unsaved objects.")
op = 'gt' if is_next else 'lt'
order = '' if is_next else '-'
param = getattr(self, field.attname)
q = Q(**{'%s__%s' % (field.name, op): param})
q = q | Q(**{field.name: param, 'pk__%s' % op: self.pk})
qs = self.__class__._default_manager.using(self._state.db).filter(**kwargs).filter(q).order_by(
'%s%s' % (order, field.name), '%spk' % order
)
try:
return qs[0]
except IndexError:
raise self.DoesNotExist("%s matching query does not exist." % self.__class__._meta.object_name)
is_next
будет True
, если вы запрашиваете get_next_by_FOO
, и False
, если вы запрашиваете get_previous_by_FOO
.
Для get_next_by_FOO
, таким образом, создается запрос, который выглядит следующим образом:
Model.objects.filter(
Q(FOO__gt=<i>current_foo</i>)
Q(FOO=<i>current_foo</i>, pk__gt=<i>current_pk</i>)
).order_by('FOO', 'pk')[0]
Таким образом, он будет искать первые Model
объекты, у которых поле FOO
такое же, но первичный ключ больше или где поле FOO
(строго) больше.
Таким образом, это приведет к тому, что запрос будет выглядеть так:
SELECT *
FROM model
WHERE FOO > <i>current_FOO</i>
OR (FOO = <i>current_FOO</i> AND id > <i>current_pk</i>)
ORDER BY FOO ASC, id ASC
LIMIT 1
Таким образом, он будет стремиться найти этот элемент, упорядочив записи и выбрав первый.
Для get_previous_by_FOO
, запрос аналогичен, за исключением того, что >
заменен на <
, и мы заказываем в обратном порядке:
SELECT *
FROM model
WHERE FOO < <i>current_FOO</i>
OR (FOO = <i>current_FOO</i> AND id < <i>current_pk</i>)
ORDER BY FOO DESC, id DESC
LIMIT 1