我有两个模型,简单的版本是这样的:
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条记录。确保每个用户至少有一个日志记录。
限制:
尝试编号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生成错误(无法解析表达式类型,未知的output_field):
logs = UserLogs.objects.filter(user_id=OuterRef('pk')).order_by('-reference_date')[:1]
users = Users.objects.annotate(latest_log = Subquery(logs))
答案 0 :(得分:0)
考虑所有限制,这似乎是不可能的。
一种方法是使用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中尝试以下SubQuery,以将queryset限制为一个元素,但是我不确定此元素的性能(信用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"
)
)