【问题标题】:Annotate objects in a queryset with next and previous object ids使用下一个和上一个对象 ID 注释查询集中的对象
【发布时间】:2018-04-25 15:52:18
【问题描述】:

如何注释 Django ORM 查询集中的对象,以便每个项目都包含之前(上一个)和之后(下一个)对象的 ID?我正在使用 PostgreSQL 和 Django 1.11。

【问题讨论】:

    标签: django postgresql orm


    【解决方案1】:

    我认为 Django 范围内稍微更详细的示例可能会对某人有所帮助。查看官方文档here

    我在视图中使用它并简单地更新响应对象:

    response_data = {}
    user = request.user
    
    # put any data in this dict according to the
    # logic you have in your view
    
    response_data.update({
        "previous_object":
            get_next_or_previous_object(user, serializer.data, next_obj=False),
        "next_object":
            get_next_or_previous_object(user, serializer.data, next_obj=True),
    })
    
    
    
    def get_next_or_previous_object(user, data, next_obj=None):
        """
        Get the previous or next object from the given data list.
    
        If it is not possible to get the object returns None
        :param data: List of objects from which the previous or next object
                     will be taken. OrderedDict and not queryset.
        :param bool next_obj: whether the next object should be returned or
                              the previous one. True = next, False = previous
        :return: the previous/next object or None
        """
    
        # Get datetime field name which will be used for obtaining the
        # next/previous object
        datetime_field_name = MyView.datetime_field_name
        object_index = 0 if next_obj else -1
    
        # Get the original object by its id
        try:
            object_id = data[object_index]["id"]
        except (IndexError, KeyError):
            object_id = None
    
        try:
            parent_object = MyModel.objects.get(id=object_id)
            if next_obj:
                # Example of the method name: get_next_by_my_timestamp_field
                next_method = getattr(
                    MyModel, f"get_next_by_{datetime_field_name}")
                object_to_get = next_method(
                    parent_object, user=user)
            else:
                # Example of the method name: get_previous_by_my_timestamp_field
                previous_method = getattr(
                    MyModel, f"get_previous_by_{datetime_field_name}")
                object_to_get = previous_method(
                    parent_object, user=user)
    
            serializer = MyView.serializer_class(object_to_get)
            previous_next_object = serializer.data
        except MyModel.DoesNotExist:
            previous_next_object = None
    
        return previous_next_object
    

    【讨论】:

      【解决方案2】:

      你可以使用window functions lag() and lead():

      SELECT *
           , lag(id)  OVER (ORDER BY id) AS prev_id  -- same order as query
           , lead(id) OVER (ORDER BY id) AS next_id  -- same order as query
      FROM   tbl
      ORDER  BY id;
      

      【讨论】:

      • 谢谢。如果我弄清楚如何使用 Django 成功地做到这一点,我可以接受这个作为答案。 Queryset.annotate(next=RawSQL(select lag(id) OVER (ORDER BY id), [])) 似乎只是返回 None
      • @jmn 这对我有用:qs = qs.annotate(next=RawSQL("lead(id) OVER (ORDER BY id)", []))
      • @devxplorer 如果我放弃选择,并像这样编写查询,我得到 ProgrammingError "window functions are not allowed in GROUP BY"
      • @jmn 告诉我们你有什么查询和你想得到什么,否则我们很难帮助你
      • 我发现因为我正在使用分页,所以在执行的 COUNT 查询中出现错误。在 shell 中使用您的查询。
      猜你喜欢
      • 2018-04-11
      • 1970-01-01
      • 2021-04-27
      • 1970-01-01
      • 2010-12-14
      • 2021-10-17
      • 2019-01-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多