如何从过滤的有序查询集中获取上一个和下一个对象?

时间:2023-01-27 16:05:47

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?

我有一个基于模型对象的页面,我想要链接到上一页和下一页。我不喜欢我当前的解决方案,因为它需要评估整个查询集(以获取ID列表),然后再两个获取查询。当然有一些方法可以一次完成吗?

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.

排序顺序在模型中定义,因此不必在此处理,但请注意,您不能假设ID是顺序的。

3 个解决方案

#1


8  

Sounds like something the Paginator set to a threshold of 1 would do well at.

听起来好像Paginator设置为1的门槛会做得很好。

# 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.

文档在这里和这里。

#2


2  

I'm new to Python and Django, so maybe my code is not optimal, but check this out:

我是Python和Django的新手,所以也许我的代码不是最优的,但请查看:

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. :)

绝对不是“重”查询集的最佳选择,但在大多数情况下,它就像魅力一样。 :)

#3


1  

Check out django-next-prev, I wrote it to solve this exact problem. In this case:

看看django-next-prev,我写了它来解决这个确切的问题。在这种情况下:

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)

#1


8  

Sounds like something the Paginator set to a threshold of 1 would do well at.

听起来好像Paginator设置为1的门槛会做得很好。

# 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.

文档在这里和这里。

#2


2  

I'm new to Python and Django, so maybe my code is not optimal, but check this out:

我是Python和Django的新手,所以也许我的代码不是最优的,但请查看:

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. :)

绝对不是“重”查询集的最佳选择,但在大多数情况下,它就像魅力一样。 :)

#3


1  

Check out django-next-prev, I wrote it to solve this exact problem. In this case:

看看django-next-prev,我写了它来解决这个确切的问题。在这种情况下:

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)