Django无外键场景下如何连接两个模型生成指定JOIN SQL语句
Django无外键关联两个模型的实现方案
针对你需要的JOIN查询需求,有两种常用的实现方式,分别对应ORM原生调用和自定义SQL调用:
方案1:ORM原生实现(无需修改模型、无需写完整SQL)
通过Subquery+OuterRef组合,可以在不定义外键的情况下完成字段匹配关联,适合需要复用ORM链式调用、分页等能力的场景:
from django.db.models import OuterRef, Subquery # 构造匹配子查询:关联Genome.query_id = Metadata.sample_id metadata_subquery = Metadata.objects.filter( sample_id=OuterRef("query_id") )[:1] # 因关联列不唯一,此处默认取匹配到的第一条,可根据业务调整筛选逻辑 # 给Genome查询结果追加关联的Metadata字段 queryset = Genome.objects.annotate( # 按需添加需要从Metadata表取的字段 related_project_id=Subquery(metadata_subquery.values("project_id")), related_sample_id=Subquery(metadata_subquery.values("sample_id")) ) # 访问示例: for obj in queryset: print(obj.query_id, obj.related_project_id)
方案2:直接使用raw方法生成你需要的精确SQL
如果需要完全匹配你给出的JOIN语句,直接调用raw()方法即可,性能最优且可控性最高:
# 执行自定义JOIN SQL,返回Genome模型实例,自动携带关联的Metadata字段 queryset = Genome.objects.raw( "SELECT A.*, B.* FROM genome AS A JOIN metadata AS B ON A.query_id = B.sample_id" ) # 访问示例: for obj in queryset: # Genome表字段直接访问 print(obj.query_id, obj.ref_id) # 关联的Metadata表字段也可直接访问 print(obj.project_id, obj.sample_id)
注意事项:
- 若两张表存在重名字段,需要在SELECT语句中手动指定别名避免字段覆盖,例如
SELECT A.id AS genome_id, B.id AS metadata_id ... - 涉及动态参数时请使用
params参数传递,避免SQL注入风险:queryset = Genome.objects.raw( "SELECT A.*, B.* FROM genome AS A JOIN metadata AS B ON A.query_id = B.sample_id WHERE B.project_id = %s", params=["目标项目ID"] )
扩展:直接获取字典格式结果
如果不需要返回模型实例,只需要原始查询结果,可以直接用数据库游标执行:
from django.db import connection with connection.cursor() as cursor: cursor.execute("SELECT A.*, B.* FROM genome AS A JOIN metadata AS B ON A.query_id = B.sample_id") # 转换为字典列表格式 columns = [col[0] for col in cursor.description] result = [dict(zip(columns, row)) for row in cursor.fetchall()]
内容的提问来源于stack exchange,提问作者Shale
相关产品推荐
相关产品推荐

