【问题标题】:Get next and previous object from QuerySet by object通过对象从 QuerySet 中获取下一个和上一个对象
【发布时间】:2019-01-30 01:07:04
【问题描述】:

我有 objectQuerySet 包含这个对象。我需要得到 下一个上一个 object 这个QuerySet

我该怎么做?

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

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

但我认为在巨大的QuerySets 上这是非常缓慢和低效的方法。

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

【问题讨论】:

  • 你为什么要这样做/?
  • 我需要在一个模型的管理员更改页面中添加下一个和上一个按钮。但它需要尊重变更列表的过滤和排序。
  • 通常你确实想使用 pagination 来实现。

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


【解决方案1】:

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

下一个:

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

上一个:

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

【讨论】:

  • 感谢 Marcell,但基本 QuerySet 不必按 id 排序。任何东西都可以订购。
【解决方案2】:

这可能就是您所需要的(在 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】:

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

    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
    

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

    【讨论】:

      【解决方案4】:

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

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

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

      第一种解决方案:

      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:
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-08-26
        • 1970-01-01
        • 2010-11-21
        • 2011-09-08
        • 1970-01-01
        • 2013-10-21
        • 2010-12-14
        • 1970-01-01
        相关资源
        最近更新 更多