关系模型增删改查
读取
通常的 get
, filter
等查询函数,并不会同时返回相关联的模型,你可以通过在 filter
时添加 prefetch_related
选项,让其同时获取相关联的模型。
| )
schools_with_students: list[School] = (
await School.select_related().prefetch_related(School.students).all()
)
school = await School.get(name="school 3")
await school.fetch_related(School.students)
assert len(school.students) == 1
|
prefetch_related
接受若干个位置参数,用于指定要同时获取的字段,如果不传入参数,则是模型上的所有关系字段。
select_related
是模型类上的没有查询条件的 filter().prefetch_related
的简写,它的参数与 prefetch_related
相同。
你也可以在模型实例上调用 fetch_related
,来让该模型实例获取与它相关联的模型,它的参数与 prefetch_related
相同。
| school = await School.get(name="school 3")
await school.fetch_related(School.students)
assert len(school.students) == 1
|
插入
insert
| school = School(name="school 1")
await school.insert()
await Student(name="student 1", school=school).insert()
|
关系字段
对于有相关关系的模型,insert
和 insert_many
只会将自身和关系模型实例之间建立关系,而不会将关系模型一起插入到数据库中,也就是说,你必须先将关系模型插入到数据库中,然后再赋值给模型实例的关系字段上。正如该例子,你需要先将 school
插入到数据库中,再将其赋值给 student
的 school
字段上。
此外,insert
接受一个类型为 bool
的 exclude_related
参数,用于指定是否要与关系模型实例建立关系,默认为 False
。
你可以使用模型实例的 insert_with_related
方法,将关系模型连同自身一起插入到数据库中:
| school2 = School(
name="school 2",
students=[
Student(name="student 2"),
Student(name="student 3"),
],
)
await school2.insert_with_related()
student4 = Student(name="student 4", school=School(name="school 3"))
await student4.insert_with_related()
|
这样就可以省去先插入关系模型的步骤了。
add
该方法仅适用于 ManyToMany
多对多关系上,它接受一个模型实例,将该模型实例添加到自己的多对多字段值上。
如果提供的模型是非多对多关系字段模型,则会抛出异常。
| tag1 = await Tag(content="tag 1").insert()
tag2 = await Tag(content="tag 2").insert()
post1 = await Post(title="post 1").insert()
post2 = await Post(title="post 2").insert()
await post1.add(tag1)
await post1.add(tag2)
await post2.add(tag1)
assert post1.tags == [tag1, tag2]
assert post2.tags == [tag1]
|
删除
对于一对多和多对多关系模型,在模型定义时有级联相关配置。
remove
对于多对多关系,可以调用 remove
来将模型实例从自己的字段上删除。
如果提供的模型是非多对多关系字段模型,则会抛出异常。
| await post1.remove(tag1)
assert post1.tags == [tag2]
await tag1.fetch_related(Tag.posts)
assert len(tag1.posts) == 1
|
完整代码
一对多完整示例代码
| from typing import Optional
import cherry
db = cherry.Database("sqlite+aiosqlite:///:memory:")
class Student(cherry.Model):
id: cherry.AutoIntPK = None
name: str
school: cherry.ForeignKey[Optional["School"]] = None
cherry_config = cherry.CherryConfig(tablename="student", database=db)
class School(cherry.Model):
id: cherry.AutoIntPK = None
name: str
students: cherry.ReverseRelation[list[Student]] = []
cherry_config = cherry.CherryConfig(tablename="school", database=db)
async def main():
await db.init()
school = School(name="school 1")
await school.insert()
await Student(name="student 1", school=school).insert()
school2 = School(
name="school 2",
students=[
Student(name="student 2"),
Student(name="student 3"),
],
)
await school2.insert_with_related()
student4 = Student(name="student 4", school=School(name="school 3"))
await student4.insert_with_related()
# Pythonic Style
student: list[Student] = await Student.filter(School.name == "school 2").all()
# Django Style
student: list[Student] = await Student.filter(school_name="school 2").all()
student_with_school: Student = (
await Student.filter(Student.name == "student 1")
.prefetch_related(Student.school)
.get()
)
school_with_students: School = (
await School.filter(School.name == "school 2")
.prefetch_related(School.students)
.get()
)
schools_with_students: list[School] = (
await School.select_related().prefetch_related(School.students).all()
)
school = await School.get(name="school 3")
await school.fetch_related(School.students)
assert len(school.students) == 1
if __name__ == "__main__":
import asyncio
asyncio.run(main())
|
多对多完整示例代码
| import cherry
db = cherry.Database("sqlite+aiosqlite:///:memory:")
class Tag(cherry.Model):
id: cherry.AutoIntPK = None
content: str
posts: cherry.ManyToMany[list["Post"]] = []
cherry_config = cherry.CherryConfig(tablename="tag", database=db)
class Post(cherry.Model):
id: cherry.AutoIntPK = None
title: str
tags: cherry.ManyToMany[list[Tag]] = []
cherry_config = cherry.CherryConfig(tablename="post", database=db)
async def main():
await db.init()
tag1 = await Tag(content="tag 1").insert()
tag2 = await Tag(content="tag 2").insert()
post1 = await Post(title="post 1").insert()
post2 = await Post(title="post 2").insert()
await post1.add(tag1)
await post1.add(tag2)
await post2.add(tag1)
assert post1.tags == [tag1, tag2]
assert post2.tags == [tag1]
await tag1.fetch_related(Tag.posts)
assert len(tag1.posts) == 2
await post1.remove(tag1)
assert post1.tags == [tag2]
await tag1.fetch_related(Tag.posts)
assert len(tag1.posts) == 1
if __name__ == "__main__":
import asyncio
asyncio.run(main())
|