关系模型定义
定义模型间关系
Cherry
支持关系型数据库的一对一、一对多以及多对多关系,只需简单的配置即可。
一对一
要声明一对一关系,只需使用 cherry.ForeignKey
注解包裹对应的模型即可,在对应的模型上则使用 cherry.ReverseRelation
来声明反向关系。
| import cherry
db = cherry.Database("sqlite+aiosqlite:///:memory:")
class UserDetail(cherry.Model):
id: cherry.AutoIntPK = None
age: int
address: str
email: str
user: cherry.ForeignKey["User"]
cherry_config = cherry.CherryConfig(tablename="user_detail", database=db)
class User(cherry.Model):
id: cherry.AutoIntPK = None
name: str
detail: cherry.ReverseRelation[UserDetail]
cherry_config = cherry.CherryConfig(tablename="user", database=db)
|
一对多
一对多和一对一关系类似,只需要把 cherry.ReverseRelation[Model]
改成 cherry.ReverseRelation[List[Model]]
,注解为模型列表即可。
| 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())
|
多对多
多对多关系关系则使用 cherry.ManyToMany
来注解。
| 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())
|
关系字段配置
ForeignKey
, ReverseRelation
, ManyToMany
等注解只能在模型主键只有一个时使用,如果是复合主键或者想要使用其他字段做外键,则需要使用 cherry.Relationship
来声明。
另外,关于表关系的级联操作等,也需要在 cherry.Relationship
中定义。
cherry.Relationship
具有以下参数:
- foreign_key - 外键目标字段。在一对一或一对多关系的外键侧表的使用,指定使用目标表的哪个字段作为外键。
- foreign_key_extra - 一些传给
sqlalchemy.ForeignKey
的额外配置。
- reverse_related - 反向关系字段。在一对一或一对多关系中的反向关系中使用,设为
True
即可。
- many_to_many - 多对多外键字段。在多对多关系中使用,指定自身模型的哪个字段为多对多关系中的外键值。
- on_update - 相关模型更新时采取的措施,来自
sqlalchemy.ForeignKey
。
- on_delete - 相关模型删除时采取的措施,来自
sqlalchemy.ForeignKey
。
- related_field - 关联的字段。通常无需你自己配置,模型会自动查找。
on_update
和 on_delete
允许的值有:
- RESTRICT - 限制更新/删除。
- CASCADE - 级联更新/删除。
- SET NULL - 设为 NULL(None),如果字段不允许为 None,则会抛出异常。
- SET DEFAULT - 设为默认值,如果字段没有默认值,则会抛出异常。
- NO ACTION - 不做任何行动,该报错就报错。
以下是两个例子。
一对一、一对多
| class Student(cherry.Model):
id: cherry.AutoIntPK = None
name: str
school: Optional["School"] = cherry.Relationship(
default=None,
foreign_key="school_id",
on_update="CASCADE",
on_delete="CASCADE",
)
cherry_config = cherry.CherryConfig(tablename="student", database=db)
class School(cherry.Model):
school_id: cherry.PrimaryKey[int]
school_name: cherry.PrimaryKey[str]
students: list[Student] = cherry.Relationship(default=[], reverse_related=True)
cherry_config = cherry.CherryConfig(tablename="school", database=db)
|
多对多
| class Tag(cherry.Model):
tag_id1: cherry.PrimaryKey[int]
tag_id2: cherry.PrimaryKey[int]
content: str
posts: list["Post"] = cherry.Relationship(
default=[],
many_to_many="post_id2",
on_delete="RESTRICT",
on_update="RESTRICT",
)
cherry_config = cherry.CherryConfig(tablename="tag", database=db)
class Post(cherry.Model):
post_id1: cherry.PrimaryKey[int]
post_id2: cherry.PrimaryKey[int]
title: str
tags: list[Tag] = cherry.Relationship(default=[], many_to_many="tag_id1")
cherry_config = cherry.CherryConfig(tablename="post", database=db)
|