【问题标题】:Django ORM: Get all records and the respective last log for each recordDjango ORM:获取所有记录以及每条记录各自的最后一个日志
【发布时间】:2020-11-07 02:44:33
【问题描述】:

我有两个模型,简单的版本是这样的:

class Users:
    name =  models.CharField()
    birthdate =  models.CharField()
    # other fields that play no role in calculations or filters, but I simply need to display

class UserLogs:
    user_id = models.ForeignKey(to='Users', related_name='user_daily_logs', on_delete=models.CASCADE)
    reference_date = models.DateField()
    hours_spent_in_chats = models.DecimalField()
    hours_spent_in_p_channels = models.DecimalField()
    hours_spent_in_challenges = models.DecimalField()
    # other fields that play no role in calculations or filters, but I simply need to display

我需要编写一个查询,它将返回所有用户的所有字段,以及每个用户的最新日志 (reference_date)。所以对于 n 个用户和 m 个日志,查询应该返回 n 条记录。保证每个用户至少有一条日志记录。

限制:

  • 查询需要用django orm编写
  • 查询需要从用户模型开始。所以任何像 Users.objects... 的东西都可以。任何像 UserLogs.objects... 的东西都不是。这是因为视图集中的过滤器和逻辑超出了我的控制范围
  • 它必须是单个查询,并且不允许在 python、pandas 或 itertools 中进行迭代。 Queryset 将由序列化程序直接处理。
  • 我不应该一一指定需要返回的列的名称。查询必须返回两个模型的所有列

尝试不。 1 仅返回用户 ID 和日志日期(原因很明显)。但是,这是正确的日期,但我只需要获取其他列:

test = User.objects.select_related("user_daily_logs").values("user_daily_logs__user_id").annotate(
    max_date=Max("user_daily_logs__reference_date"))

尝试不。 2 生成错误(无法解析表达式类型,未知输出字段):

logs = UserLogs.objects.filter(user_id=OuterRef('pk')).order_by('-reference_date')[:1]
users = Users.objects.annotate(latest_log = Subquery(logs))

【问题讨论】:

    标签: django django-rest-framework django-orm


    【解决方案1】:

    考虑到所有限制,这似乎是不可能的。

    一种方法是使用prefetch_related

    users = User.objects.all().prefetch_related(
        models.Prefetch(
            'user_daily_logs',
            queryset=UserLogs.objects.filter().order_by('-reference_date'),
            to_attr="latest_log"
        )
    )
    

    这将执行两个数据库查询并返回每个用户的所有日志,这取决于记录的数量可能会或不会出现问题。如果顾名思义,您只需要当天的日志,您可以添加它来过滤和减少 UserLogs 记录的数量。当然你需要从列表中获取第一个元素。

    users.daily_logs[0]
    

    为此,您可以在 User 模型上创建一个 @property,大致如下所示

    @property
    def latest_log(self):
        if not hasattr('daily_logs'):
            return None
        return self.daily_logs[0]
     
    user.latest_log
    

    您还可以更进一步,在 Prefetch 中尝试以下子查询,以将查询集限制为一个元素,但我不确定这个元素的性能(学分 Django prefetch_related with limit)。

    users = User.objects.all().prefetch_related(
        models.Prefetch(
            'user_daily_logs',
            queryset=UserLogs.objects.filter(id__in=Subquery(UserLogs.objects.filter(user_id=OuterRef('user_id')).order_by('-reference_date').values_list('id', flat=True)[:1] ) ),
            to_attr="latest_log"
        )
    )
    

    【讨论】:

      猜你喜欢
      • 2017-02-19
      • 2020-10-01
      • 1970-01-01
      • 2022-10-14
      • 2020-04-09
      • 2013-07-27
      • 2011-12-17
      • 2015-09-29
      • 1970-01-01
      相关资源
      最近更新 更多