Django模型如何实现含空值的两表左连接查询
Django左连接查询实现方案
首先纠正你原SQL的小问题:原WHERE子句会过滤掉无当日活动记录的用户(因为Daily.day为NULL时不满足等于今日的条件),正确的SQL应该将日期条件放到JOIN的ON子句中:
SELECT User.id, User.username, Daily.id as daily_id FROM User LEFT JOIN UserDailyActivity as Daily on (User.id = Daily.user_id AND Daily.day = TODAY())
以下是两种Django ORM的实现方式:
方法一:Subquery + OuterRef(推荐)
通过子查询获取每个用户当日的活动ID,无匹配时返回NULL:
from django.db.models import Subquery, OuterRef, IntegerField from datetime import date today = date.today() # 子查询:筛选当前用户当日的第一条活动记录ID daily_subquery = UserDailyActivity.objects.filter( user=OuterRef('pk'), day=today ).values('id')[:1] # 限制1条,避免多匹配导致错误 # 为用户模型标注当日活动ID user_queryset = User.objects.annotate( daily_id=Subquery(daily_subquery, output_field=IntegerField()) ).values('id', 'username', 'daily_id') # 遍历结果 for item in user_queryset: print(item)
方法二:FilteredRelation + Case/When
利用Django的FilteredRelation定义带条件的关联,再通过条件判断获取ID:
from django.db.models import FilteredRelation, Q, Case, When, IntegerField from datetime import date today = date.today() user_queryset = User.objects.annotate( # 定义过滤后的当日活动关联 today_activity=FilteredRelation( 'userdailyactivity_set', # 反向关联名,默认是模型名小写+_set,若外键设置了related_name则用对应名称 condition=Q(day=today) ) ).annotate( # 根据关联是否存在赋值daily_id daily_id=Case( When(today_activity__isnull=False, then='today_activity__id'), default=None, output_field=IntegerField() ) ).values('id', 'username', 'daily_id')
说明
- 若你之前尝试
prefetch_related失败,是因为它仅用于批量预加载关联对象,无法直接将关联字段作为查询结果的一部分返回; annotation单独使用时若未限制结果数量,可能因同一用户多条当日活动导致重复数据,用Subquery[:1]可避免此问题。
内容的提问来源于stack exchange,提问作者Xavier M
相关产品推荐
相关产品推荐

