【问题标题】:Getting next and previous objects in Django在 Django 中获取下一个和上一个对象
【发布时间】:2011-08-26 16:10:47
【问题描述】:

我正在尝试获取漫画书问题的下一个和上一个对象。简单地更改 ID 号或通过添加日期进行过滤是行不通的,因为我没有按顺序添加问题。

这就是我的视图的设置方式,它适用于 prev_issue 并返回前一个对象,但它返回 next_issue 的最后一个对象,我不知道为什么。

def issue(request, issue_id):
    issue = get_object_or_404(Issue, pk=issue_id)
    title = Title.objects.filter(issue=issue)
    prev_issue = Issue.objects.filter(title=title).filter(number__lt=issue.number)[0:1]
    next_issue = Issue.objects.filter(title=title).filter(number__gt=issue.number)[0:1]

【问题讨论】:

  • 你想用上一期/下一期的信息做什么?您是否打算在页面上链接到它们,在列表中显示它们等? issue_ids 是否为每个标题过滤过一次?
  • 只是一个奇怪的问题:您是在模型中使用 get_absolute_url 属性还是只是将 prev_issue.issue_id 重定向到 url?
  • 我使用的是get_absolute_url 属性...绝对不是issue_id,因为我不会一直按顺序添加问题,这不切实际。

标签: django django-views


【解决方案1】:

注意不要使用object.get(pk=object.pk + 1)这些东西,IntegrityError如果该pk处的对象被删除,则始终使用查询集

对于访客:

''' Useage '''
"""
    # Declare our item
    store = Store.objects.get(pk=pk)
    # Define our models
    stores = Store.objects.all()
    # Ask for the next item
    new_store = get_next_or_prev(stores, store, 'next')
    # If there is a next item
    if new_store:
        # Replace our item with the next one
        store = new_store
"""

''' Function '''
def get_next_or_prev(models, item, direction):
    '''
    Returns the next or previous item of
    a query-set for 'item'.

    'models' is a query-set containing all
    items of which 'item' is a part of.

    direction is 'next' or 'prev'
    
    '''

    getit = False
    if direction == 'prev':
        models = models.reverse()
    for m in models:
        if getit:
            return m
        if item == m:
            getit = True
    if getit:
        # This would happen when the last
        # item made getit True
        return models[0]
    return False

original author

用法

# you MUST call order by to pass in an order, otherwise QuerySet.reverse will not work
qs = Model.objects.all().order_by('pk')
q = qs[0]
prev = get_next_or_prev(qs, q, 'prev')
next = get_next_or_prev(qs, q, 'next')

【讨论】:

    【解决方案2】:

    next_obj_id = int(current_obj_id) + 1 next_obj = Model.objects.filter(id=next_obj_id).first()

    prev_obj_id= int(current_obj_id) - 1 prev_obj = Model.objects.filter(id=prev_obj_id).first()

    #你没有什么可以放松的......这对我有用

    【讨论】:

    • OP 的代码中不存在变量current_obj_id
    • 这也假设您的 id 是连续的,当结果来自 filter 查询时,这不需要是真的。
    【解决方案3】:
    from functools import partial, reduce
    from django.db import models
    
    
    def next_or_prev_instance(instance, qs=None, prev=False, loop=False):
    
        if not qs:
            qs = instance.__class__.objects.all()
    
        if prev:
            qs = qs.reverse()
            lookup = 'lt'
        else:
            lookup = 'gt'
    
        q_list = []
        prev_fields = []
    
        if qs.query.extra_order_by:
            ordering = qs.query.extra_order_by
        elif qs.query.order_by:
            ordering = qs.query.order_by
        elif qs.query.get_meta().ordering:
            ordering = qs.query.get_meta().ordering
        else:
            ordering = []
    
        ordering = list(ordering)
    
        if 'pk' not in ordering and '-pk' not in ordering:
            ordering.append('pk')
            qs = qs.order_by(*ordering)
    
        for field in ordering:
            if field[0] == '-':
                this_lookup = (lookup == 'gt' and 'lt' or 'gt')
                field = field[1:]
            else:
                this_lookup = lookup
            q_kwargs = dict([(f, get_model_attr(instance, f))
                             for f in prev_fields])
            key = "%s__%s" % (field, this_lookup)
            q_kwargs[key] = get_model_attr(instance, field)
            q_list.append(models.Q(**q_kwargs))
            prev_fields.append(field)
        try:
            return qs.filter(reduce(models.Q.__or__, q_list))[0]
        except IndexError:
            length = qs.count()
            if loop and length > 1:
                return qs[0]
        return None
    
    
    next_instance = partial(next_or_prev_instance, prev=False)
    prev_instance = partial(next_or_prev_instance, prev=True)
    

    【讨论】:

      【解决方案4】:

      添加一个order_by 子句以确保它按number 排序。

      next_issue = Issue.objects.filter(title=title, number__gt=issue.number).order_by('number').first()
      

      【讨论】:

      • 您也应该对 prev_issue 查询执行此操作。乐于助人!
      • 我想知道这样的命令会从数据库中检索多少条记录。只有一条记录还是所有满足过滤条件的记录(我们只选择其中一条)?如果是后一种情况,我们怎样才能提高效率?
      • 如果数据库支持,切片语法 ([0:1]) 将使用“限制”查询 - 所以在 MySQL 上应该没问题。 docs.djangoproject.com/en/dev/topics/db/queries/…
      • 确保“数字”字段有索引。
      • 过滤器不用两次,可以filter(title=title, number__gt=issue.number)
      【解决方案5】:

      如果需要查找按可以相等的字段值排序的下一个上一个对象,并且这些字段不属于Date*类型,查询变得稍微复杂,因为:

      • 对受[:1] 限制的具有相同值的对象进行排序将始终为多个对象产生相同的结果;
      • 对象本身可以包含在结果集中。

      这里的查询集还考虑了产生正确结果的主键(假设来自 OP 的 number 参数不是唯一的,并且省略了 title 参数,因为它与示例无关):

      上一个:

      prev_issue = (Issue.objects
          .filter(number__lte=issue.number, id__lt=instance.id)
          .exclude(id=issue.id)
          .order_by('-number', '-id')
          .first())
      

      下一步:

      next_issue = (Issue.objects
          .filter(number__gte=issue.number, id__gt=instance.id)
          .exclude(id=issue.id)
          .order_by('number', 'id')
          .first())
      

      【讨论】:

        【解决方案6】:

        我知道这有点晚了,但对于其他人来说,django 确实有更好的方法来做到这一点,请参阅https://docs.djangoproject.com/en/1.7/ref/models/instances/#django.db.models.Model.get_previous_by_FOO

        所以这里的答案应该是

        next_issue = Issue.get_next_by_number(issue, title=title)
        

        Django 管理器用一点元类的技巧来做到这一点。

        【讨论】:

        • 请注意,这只适用于非空 DateField 和 DateTimeField。
        • 我认为它是在 Django 核心级别上完成的,因为开发人员可能会遇到由于可能的 Date 或 DateTime 相等性而遇到的困难。条件更改为__gte__lte,这会导致结果查询更加复杂。通过Date* 字段处理上一个和下一个对象时,此内置是最佳选择。
        • 最酷的解决方案。竖起大拇指(y)
        • 更新链接,因为原始链接已损坏:docs.djangoproject.com/en/3.1/ref/models/instances/…
        猜你喜欢
        • 1970-01-01
        • 2019-01-30
        • 1970-01-01
        • 2013-10-21
        • 1970-01-01
        • 1970-01-01
        • 2019-04-17
        • 2016-01-27
        • 2017-07-02
        相关资源
        最近更新 更多