有没有聪明的方法通过Django ORM获取前一个/下一个项目?

22 投票
3 回答
9028 浏览
提问于 2025-04-15 17:11

假设我有一组按创建日期排序的照片,内容如下:

class Photo(models.Model):
    title = models.Char()
    image = models.Image()
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('-created',)

我有一个任意的照片对象 photo_x。有没有简单的方法可以根据在这个照片列表中的位置找到前一张和后一张照片?另外,如果我在列表的开头或结尾时,能不能让它循环回去?而且如果只有一张或两张照片时,也不要出错。

3 个回答

-1

对于jathanism的回答,这个方法很有用,但get_next_by_FOOget_previous_by_FOO会忽略毫秒的部分……举个例子,它在处理循环中创建的对象时就不太管用:

for i in range(100):
    Photo.objects.create(title='bla', ...)

obj = Photo.objects.first()
obj.get_previous_by_created()

DoesNotExist: Photo matching query does not exist.
5

get_next_by_foo 和 get_previous_by_foo 这两个功能很方便,但也有些限制——如果你要根据多个字段排序,或者排序的字段不是日期,这两个功能就帮不上忙了。

我写了一个叫 django-next-prev 的工具,它能更灵活地实现类似的功能。在你的情况下,你可以直接这样做,因为你已经在 Meta 里设置了排序:

from next_prev import next_in_order, prev_in_order
from .models import Photo

photo = Photo.objects.get(...)
next = next_in_order(photo)
prev = prev_in_order(photo)

如果你想根据其他字段的组合来排序,只需传入查询集(queryset):

photos = Photo.objects.order_by('title')
photo = photos.get(...)
next = next_in_order(photo, qs=photos)
36

你真幸运!Django 默认会为 DateFieldDateTimeField 创建 get_next_by_fooget_previous_by_foo 这两个方法,只要它们没有设置 null=True

举个例子:

>>> from foo.models import Request
>>> r = Request.objects.get(id=1)
>>> r.get_next_by_created()
<Request: xyz246>

如果你到达了一组数据的末尾,它会抛出一个 DoesNotExist 的异常,这个异常你可以用来触发返回到这组数据的开头:

>>> r2 = r.get_next_by_created()
>>> r2.get_next_by_created()
...
DoesNotExist: Request matching query does not exist.

想了解更多内容,可以查看:额外的实例方法

撰写回答