ORM 中的 related_name、正向关系和反向关系详解
related_name 的定义
related_name
是在定义模型关系时使用的一个参数,它指定了从关联模型反向引用当前模型时使用的名称。
正向关系 vs 反向关系
为了理解这些概念,让我们使用一个具体的例子:
from tortoise import Model, fields
class Author(Model):
name = fields.CharField(max_length=100)
class Book(Model):
title = fields.CharField(max_length=200)
author = fields.ForeignKeyField('models.Author', related_name='books')
正向关系
- 定义:从定义外键的模型到被引用的模型的关系。
- 示例:从
Book
到Author
的关系是正向关系。 - 使用方式:直接通过外键字段名访问。
# 正向关系查询
book = await Book.get(id=1)
author = await book.author
反向关系
- 定义:从被引用的模型回到定义外键的模型的关系。
- 示例:从
Author
到Book
的关系是反向关系。 - 使用方式:通过
related_name
指定的名称访问。
# 反向关系查询
author = await Author.get(id=1)
books = await author.books.all() # 'books' 是由 related_name 定义的
related_name 的作用
-
定义反向关系的访问名称:
- 如果不指定
related_name
,Tortoise ORM 会使用默认名称(模型名小写 + “_set”)。 - 在我们的例子中,如果不指定
related_name='books'
,默认会是book_set
。
- 如果不指定
-
避免命名冲突:
- 当一个模型有多个指向同一个模型的外键时,
related_name
可以避免冲突。
- 当一个模型有多个指向同一个模型的外键时,
-
提高代码可读性:
- 使用描述性的
related_name
可以使代码更加直观和自解释。
- 使用描述性的
示例说明
# 创建实例
author = await Author.create(name="J.K. Rowling")
book = await Book.create(title="Harry Potter", author=author)
# 正向关系查询
book = await Book.get(title="Harry Potter")
author_name = await book.author.name # 使用正向关系
# 反向关系查询
author = await Author.get(name="J.K. Rowling")
books = await author.books.all() # 使用反向关系,通过 related_name 访问
不同类型关系中的 related_name
-
一对多关系(如上例):
related_name
定义在 “多” 的一方,用于从 “一” 的一方访问相关的多个对象。
-
多对多关系:
class Student(Model): name = fields.CharField(max_length=100) class Course(Model): name = fields.CharField(max_length=100) students = fields.ManyToManyField('models.Student', related_name='courses')
- 这里,
students
和courses
都可以用来进行反向查询。
- 这里,
-
一对一关系:
class User(Model): name = fields.CharField(max_length=100) class Profile(Model): user = fields.OneToOneField('models.User', related_name='profile') bio = fields.TextField()
related_name
允许从User
直接访问关联的Profile
。
注意事项
-
唯一性:在同一个模型中,不同关系的
related_name
必须唯一。 -
反向关系的查询集:通过
related_name
访问的反向关系通常返回一个查询集,除非是一对一关系。 -
性能考虑:使用
prefetch_related
可以优化反向关系的查询性能。
authors_with_books = await Author.all().prefetch_related('books')
- 命名约定:通常使用复数形式作为
related_name
,特别是在一对多和多对多关系中。