如何在Django中基于多字段高效实现左外连接?
Django ORM实现带额外条件的左外连接
问题背景
现有如下Django模型:
class Author(models.Model): name = models.CharField(max_length=100, primary_key=True) date = models.CharField() other_field = models.CharField() # 注:目标SQL中用到了author.email,需确认模型是否遗漏该字段,建议补充 # email = models.CharField(max_length=100) class Book(models.Model): title = models.CharField(max_length=200) author = models.ForeignKey(Author, on_delete=models.CASCADE) publication_date = models.CharField()
需要生成包含额外连接条件的左外连接SQL:
select title, author, name as nam from book left outer join author on (book.author = author.id AND book.title = author.email)
尝试使用FilteredRelation未成功,询问是否可通过Django ORM实现。
可行方案
方案1:使用FilteredRelation(推荐,纯ORM写法)
FilteredRelation是Django 2.0+提供的功能,可给关联关系添加额外过滤条件,生成带多条件的左连接:
from django.db.models import FilteredRelation, Q, F # 生成符合需求的查询集 books = Book.objects.annotate( # 给author关联添加额外条件:book.title = author.email matched_author=FilteredRelation( 'author', condition=Q(author__email=F('title')) ) ).values( 'title', 'author_id', # 对应SQL中的book.author字段 nam=F('matched_author__name') # 别名nam对应author.name )
该写法生成的SQL会自动包含LEFT OUTER JOIN author ON (book.author_id = author.name AND book.title = author.email),与目标SQL结构一致。
方案2:使用extra()手动指定连接
如果需要更贴近原生SQL的写法,可使用extra()直接定义连接表、条件和字段别名:
books = Book.objects.extra( tables=['author'], where=['book.author_id = author.name AND book.title = author.email'], select={'nam': 'author.name'} ).values('title', 'author_id', 'nam')
方案3:原生SQL查询
如果上述ORM写法无法满足需求,可直接使用raw()执行原生SQL:
books = Book.objects.raw(''' select title, author_id as author, name as nam from book left outer join author on (book.author_id = author.name AND book.title = author.email) ''')
注意事项
- 目标SQL中用到
author.email,但当前Author模型未定义该字段,需补充字段或调整条件中的字段名。 - Django ForeignKey默认存储关联主键的字段名为
{关联模型名}_id,即book.author_id对应Author的主键name(因为Author将name设为primary_key)。
内容的提问来源于stack exchange,提问作者Abdul Rahman.
相关产品推荐
相关产品推荐

