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

时间:2018-08-23 15:34:04

标签: python django django-queryset django-orm django-2.1

我有objectQuerySet,其中包含此对象。我需要获得此object下一个上一个 QuerySet

我该怎么做?

我可以这样下一个

next = False
for o in QuerySet:
    if next:
        return o
    if o==object:
       next = True

但是我认为在大型QuerySet上这是一种非常缓慢且效率低下的方法。

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

4 个答案:

答案 0 :(得分:1)

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

第一个更优雅,但性能稍差。第二个明显更快,特别是对于较大的 QuerySets,但它结合使用原始 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:

答案 1 :(得分:0)

使用Django QuerySet API,您可以尝试以下操作:

对于下一个:

qs.filter(pk__gt=obj.pk).order_by('pk').first()

对于上一个:

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

答案 2 :(得分:0)

可能就是您所需要的(在Python 3中,如果您需要Python 2.7的解决方案,请告诉我):

def get_next(queryset, obj):
    it = iter(queryset)
    while obj is not next(it):
        pass
    try:
        return next(it)
    except StopIteraction:
        return None

def get_prev(queryset, obj):
    prev = None
    for o in queryset:
        if o is obj:
            break
        prev = obj
    return prev

但是有一些注意事项:

  1. 只要完整的查询集存储在变量中,您就可以保留对象的索引并提取下一个和上一个作为[i + 1][i - 1]。否则,您必须遍历整个queryset才能在此处找到对象。
  2. 根据PEP8,您不应命名诸如QuerySet之类的变量,此类名称应用于类。将其命名为queryset

答案 3 :(得分:0)

也许您可以使用类似的东西:

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

它不是很漂亮,但是应该可以工作...