You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 16:25:20