导读:本期聚焦于坚哥创作的《Django怎么反向查询?related_name参数与_set管理器使用详解》,敬请观看详情。外键关联的两张表之间,除了从子表查主表,还经常需要从主表反过来查子表的数据,这就是Django中的反向查询。本文围绕反向查询的两种典型实现方式展开:一是默认的下划线加set管理器,二是通过外键定义时的related_name参数自定义访问名称。文章会讲解两种方式在查询语法上的差异、related_name设置为加号时屏蔽反向查询的用法、反向查询配合filter和values进行条件过滤的写法,以及在Django REST Framework序列化器中如何利用related_name简化嵌套数据的输出。同时分析常见报错信息和排查思路,帮助你在外键设计阶段就规划好双向访问的命名规范。

Django的ORM在处理多表关联时非常强大,但不少人在写了一段时间正向查询之后,第一次接触反向查询时会感到困惑:明明模型之间有外键关联,为什么没法用属性直接访问关联数据?其实反向查询有两种完全不同的写法,一种依赖默认的_set管理器,另一种则由外键字段上的related_name参数决定。理解这两者的关系,是掌握Django多表查询绕不开的一步。

Django怎么反向查询?related_name参数与_set管理器使用详解

什么是反向查询,它与正向查询的区别在哪里

先看一个最典型的场景:作者和书籍。一个作者可以写多本书,书籍模型通过外键指向作者模型。从书籍找作者是正向查询,因为外键就定义在书籍模型上;从作者找他写的所有书则是反向查询,因为作者模型上并没有显式定义任何关联字段,Django是在运行时动态创建访问入口的。

正向查询的写法很直观,直接点外键字段名即可:

class Author(models.Model):
    name = models.CharField(max_length=50)

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

# 正向查询:通过书找作者
book = Book.objects.get(id=1)
print(book.author.name)

而反向查询不能写author.book,因为Author模型上根本不存在book这个字段。Django的规则是:默认情况下,在关联模型实例上通过小写模型名加_set的形式访问,也就是book_set。它本质上是一个RelatedManager,行为和普通的objects管理器类似,支持all、filter、count等方法。

# 反向查询:通过作者找所有书
author = Author.objects.get(id=1)
books = author.book_set.all()
print(books.count())

# 也可以直接过滤
python_books = author.book_set.filter(title__contains='Python')

需要注意一点,book_set中的book是模型名的小写形式。如果模型名由多个单词组成,比如BookChapter,默认的反向名称就是bookchapter_set,模型名会被转成小写再拼接_set。这一点经常被人忽略,导致在模板或视图中写出BookChapter_set这种大小写不一致的写法而报AttributeError。

related_name参数:自定义反向查询的访问名称

如果觉得book_set这种命名不够直观,可以在定义外键时通过related_name参数指定一个更语义化的名称。设置之后,原来的_set写法就完全失效,只能使用你指定的名称访问。

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.ForeignKey(
        Author,
        on_delete=models.CASCADE,
        related_name='books'
    )

# 使用自定义名称反向查询
author = Author.objects.get(id=1)
all_books = author.books.all()
recent_books = author.books.filter(create_time__year=2024)

related_name的命名建议用复数形式,因为它返回的是一个集合。比如作者的书用books,分类下的商品用products,这样代码读起来更接近自然语言:author.books.all()一眼就能看出是取某作者的全部书籍。

还有一个特殊的用法值得单独说明:把related_name设置成加号(+)可以彻底关闭反向查询入口。这在某些业务场景下很有用,比如日志表通过外键关联用户表,但你永远不需要从用户反向查日志,加上related_name='+'之后,User实例上就不会创建log_set管理器,避免误用。

class OperationLog(models.Model):
    user = models.ForeignKey(
        'auth.User',
        on_delete=models.CASCADE,
        related_name='+'
    )
# 此时user.operationlog_set 会直接报错

另外,当同一个模型被另一个模型多次引用时,related_name就不再是可选项而是必填项。例如文章模型同时有作者和编辑两个外键都指向User,如果不分别指定related_name,Django在做模型检查时会抛出冲突错误,提示反向查询名称重复。正确写法如下:

class Article(models.Model):
    author = models.ForeignKey(
        'auth.User',
        on_delete=models.CASCADE,
        related_name='authored_articles'
    )
    editor = models.ForeignKey(
        'auth.User',
        on_delete=models.SET_NULL,
        null=True,
        related_name='edited_articles'
    )

在filter和序列化中运用反向查询

反向查询不仅能在实例上使用,在查询集的条件过滤中也扮演重要角色,而且语法不同。实例上用的是book_set,但在filter里要使用小写模型名(或related_name指定的名称),不需要加_set后缀。这是最容易混淆的地方,很多初学者会写成filter(book_set__title='xxx')然后报FieldError。

# 未设置related_name时,跨表过滤用小写模型名
authors = Author.objects.filter(book__title__contains='Django')

# 设置related_name='books'后,用自定义名称
authors = Author.objects.filter(books__title__contains='Django')

# 统计每个作者的书数量并排序
from django.db.models import Count
ranking = Author.objects.annotate(
    total=Count('books')
).order_by('-total')

在Django REST Framework中,related_name的价值更加明显。如果想在作者接口中直接嵌套返回所有书籍,序列化器只需按反向名称声明一个字段,框架会自动处理嵌套输出:

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['id', 'title']

class AuthorSerializer(serializers.ModelSerializer):
    books = BookSerializer(many=True, read_only=True)

    class Meta:
        model = Author
        fields = ['id', 'name', 'books']

这样请求作者详情时,返回的JSON里会自动带上一个books数组,无需在视图中手动组装数据。如果用的是默认的_set名称,也可以在序列化器里用source参数指定,写成BookSerializer(source='book_set', many=True, read_only=True),效果完全相同。

最后提醒一个性能问题:无论是book_set.all()还是序列化器里的嵌套读取,只要在循环中逐个访问都会触发N+1查询。解决办法是在查询入口处用prefetch_related预加载反向关联数据,例如Author.objects.prefetch_related('books'),它会把所有关联书籍一次性查出来放入缓存,循环访问时不再重复打数据库。正向外键用select_related,反向一对多用prefetch_related,这个搭配习惯建议在项目初期就固定下来。

Django反向查询related_name_set管理器修改时间:2026-09-11 09:42:37

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0911/54593.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。