Python 按对象从QuerySet获取下一个和上一个对象

Python 按对象从QuerySet获取下一个和上一个对象,python,django,django-queryset,django-orm,django-2.1,Python,Django,Django Queryset,Django Orm,Django 2.1,我有和对象和查询集,其中包含此对象。我需要获取此查询集的下一个和上一个对象 我该怎么做 我可以通过以下方式获得下一个: next = False for o in QuerySet: if next: return o if o==object: next = True 但我认为,在庞大的QuerySets上,这种方法非常缓慢且效率低下 您知道更好的解决方案吗?使用Django QuerySet API您可以尝试以下方法: 下一步: qs.filt

我有和
对象
查询集
,其中包含此对象。我需要获取此
查询集的下一个上一个
对象

我该怎么做

我可以通过以下方式获得下一个

next = False
for o in QuerySet:
    if next:
        return o
    if o==object:
       next = True
但我认为,在庞大的
QuerySet
s上,这种方法非常缓慢且效率低下


您知道更好的解决方案吗?

使用
Django QuerySet API
您可以尝试以下方法:

下一步:

qs.filter(pk\u gt=obj.pk).order\u by('pk').first()

对于以前的:


qs.filter(pk_ult=obj.pk).order_by('-pk').first()

这可能就是您需要的(在Python3中,如果您需要Python2.7的解决方案,请告诉我):

但也有一些注意事项:

  • 只要将完整查询集存储到变量中,您就可以保留对象的索引,并将下一个和上一个对象提取为
    [i+1]
    [i-1]
    。否则,您必须遍历整个
    queryset
    ,才能在那里找到您的对象
  • 根据PEP8,您不应该像
    QuerySet
    那样命名变量,这样的名称应该用于类。命名它
    queryset

  • 也许你可以用这样的东西:

    def get_prev_next(obj, qset):
        assert obj in qset
        qset = list(qset)
        obj_index = qset.index(obj)
        try:
            previous = qset[obj_index-1]
        except IndexError:
            previous = None    
        try:
            next = qset[obj_index+1]
        except IndexError:
            next = None
        return previous,next
    

    这不是很漂亮,但应该有用…

    我知道这个问题有点老了,但我遇到了这个问题,没有找到非常有效的解决方案,所以我希望它可以帮助别人。 我想出了两个非常好的解决方案

    第一个更优雅,但性能稍差。第二个明显更快,特别是对于较大的查询集,但它使用原始SQL进行组合

    它们都可以找到上一个和下一个ID,但当然可以调整以检索实际的对象实例

    第一种解决方案:

    object_ids = list(filtered_objects.values_list('id', flat=True))
    current_pos = object_ids.index(my_object.id)
    if current_pos < len(object_ids) - 1:
        next_id = object_ids[current_pos + 1]
    if current_pos > 0:
        previous_id = object_ids[current_pos - 1]
    
    window = {'order_by': ordering_fields}
    with_neighbor_objects = filtered_objects.annotate(
        next_id=Window(
            Lead('id'),
            **window
        ),
        previous_id=Window(
            Lag('id'),
            **window
        ),
    )
    sql, params = with_neighbor_objects.query.sql_with_params()
    #  wrap the windowed query with another query using raw SQL, as
    #  simply using .filter() will destroy the window, as the query itself will change.
    current_object_with_neighbors = next(r for r in filtered_objects.raw(f"""
            SELECT id, previous_id, next_id FROM ({sql}) filtered_objects_table
            WHERE id=%s
        """, [*params, object_id]))
    
    next_id = current_object_with_neighbors.next_id:
    previous_id = current_object_with_neighbors.previous_id:
    

    谢谢Marcell,但基本查询集不必按id排序。它可以按任何方式排序。为什么要这样做/?我需要为一个型号的管理员更改页面添加下一个和上一个按钮。但它需要尊重变更列表的过滤和排序。通常您确实希望为此使用分页。
    window = {'order_by': ordering_fields}
    with_neighbor_objects = filtered_objects.annotate(
        next_id=Window(
            Lead('id'),
            **window
        ),
        previous_id=Window(
            Lag('id'),
            **window
        ),
    )
    sql, params = with_neighbor_objects.query.sql_with_params()
    #  wrap the windowed query with another query using raw SQL, as
    #  simply using .filter() will destroy the window, as the query itself will change.
    current_object_with_neighbors = next(r for r in filtered_objects.raw(f"""
            SELECT id, previous_id, next_id FROM ({sql}) filtered_objects_table
            WHERE id=%s
        """, [*params, object_id]))
    
    next_id = current_object_with_neighbors.next_id:
    previous_id = current_object_with_neighbors.previous_id: