型号
定义模型
通过定义 ormar 模型,您可以免费获得相应的 Pydantic 模型以及 Sqlalchemy 表。它们是在后台管理的,您不必自己创建它们。
模型类
要构建 ormar 模型,您只需继承 ormar.Model 类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
|
定义字段
接下来将一个或多个字段分配为类级别变量。
基本字段类型
每个表都必须有一个主键列,您可以通过在选定字段上设置 Primary_key=True 来指定该主键列。
只允许有一个主键列。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
|
!!!警告 不分配 Primary_key 列或为每个模型分配多个列将引发 ModelDefinitionError 异常。
默认情况下,如果将主键分配给整数字段,则自动增量选项设置为 true。
您可以通过传递 autoincrement=False 来禁用。
| id: int = ormar.Integer(primary_key=True, autoincrement=False)
|
非数据库字段
请注意,如果您的模型中需要一个普通的 pydantic 字段(用于在模型上存储值或传递某些值),您可以像平常一样在 pydantic 中定义一个字段。
像这样创建的字段将添加到 pydantic 模型字段 -> 因此根据字段类型进行验证,也出现在 model_dump() 和 model_dump_json() 结果中。
不同之处在于这些字段不保存在数据库中。因此它们不会包含在底层 sqlalchemy 列或表变量中(请检查下面的内部部分以了解如何在需要时访问这些内容)。
随后,pydantic 字段将不会包含在迁移或任何数据库操作(如保存、更新等)中。
像这样的字段可以传递到 fastapi 请求中的有效负载中,并将在 fastapi 响应中返回(当然,只有当您在代码中的某个位置设置它们的值时,因为该值不是从数据库中获取的。如果您在 fastapirequest 中传递一个值并且返回 fastapi 在 request_model 中为您构造的相同实例,您应该在响应中返回完全相同的值。)。
!!! warning pydantic 字段必须始终是可选的,并且不能更改(否则数据库加载验证将失败)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | import databases
import ormar
import pydantic
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
non_db_field: str = pydantic.Field(max_length=100)
|
如果您使用默认参数设置 pydantic 字段并且不在请求中传递实际值,您将始终获得默认值。由于它可以是一个函数,您可以设置 default=datetime.datetime.now 并在每次调用端点等时获取当前时间戳。
Fastapi 中的非数据库字段
注意,pydantic 和calculated_fields 修饰字段都可以包含/排除在model_dump() 和fastapi 响应中,相应地包含/排除和response_model_include/response_model_exclude。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 | # <==part of related code removed for clarity==>
base_ormar_config = ormar.OrmarConfig(
database=databases.Database(DATABASE_URL),
metadata=sqlalchemy.MetaData(),
engine=sqlalchemy.create_engine(DATABASE_URL),
)
class User(ormar.Model):
ormar_config = base_ormar_config.copy(tablename="users2")
id: int = ormar.Integer(primary_key=True)
email: str = ormar.String(max_length=255, nullable=False)
password: str = ormar.String(max_length=255)
first_name: str = ormar.String(max_length=255)
last_name: str = ormar.String(max_length=255)
category: str = ormar.String(max_length=255, nullable=True)
timestamp: datetime.datetime = pydantic.Field(
default=datetime.datetime.now
)
# <==part of related code removed for clarity==>
app = FastAPI()
@app.post("/users/")
async def create_user(user: User):
return await user.save()
# <==part of related code removed for clarity==>
def test_excluding_fields_in_endpoints():
client = TestClient(app)
with client as client:
timestamp = datetime.datetime.now()
user = {
"email": "test@domain.com",
"password": "^*^%A*DA*IAAA",
"first_name": "John",
"last_name": "Doe",
"timestamp": str(timestamp),
}
response = client.post("/users/", json=user)
assert list(response.json().keys()) == [
"id",
"email",
"first_name",
"last_name",
"category",
"timestamp",
]
# returned is the same timestamp
assert response.json().get("timestamp") == str(timestamp).replace(" ", "T")
# <==part of related code removed for clarity==>
|
字段名称与列名称
默认情况下,字段名称将用于底层 pydantic 模型和 sqlalchemy 表。
如果出于某种原因您希望更改数据库中的名称但将名称保留在模型中,您可以通过在字段声明期间指定名称参数来完成此操作
这里有一个名称已更改的示例模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | import databases
import ormar
import sqlalchemy
DATABASE_URl = "sqlite:///test.db"
database = databases.Database(DATABASE_URl, force_rollback=True)
metadata = sqlalchemy.MetaData()
class Child(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
tablename="children",
)
id: int = ormar.Integer(name="child_id", primary_key=True)
first_name: str = ormar.String(name="fname", max_length=100)
last_name: str = ormar.String(name="lname", max_length=100)
born_year: int = ormar.Integer(name="year_born", nullable=True)
|
请注意,您还可以更改外键列名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 | from typing import Optional
import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///test.db", force_rollback=True)
metadata = sqlalchemy.MetaData()
class Artist(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
tablename="artists",
)
id: int = ormar.Integer(name="artist_id", primary_key=True)
first_name: str = ormar.String(name="fname", max_length=100)
last_name: str = ormar.String(name="lname", max_length=100)
born_year: int = ormar.Integer(name="year")
class Album(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
tablename="music_albums",
)
id: int = ormar.Integer(name="album_id", primary_key=True)
name: str = ormar.String(name="album_name", max_length=100)
artist: Optional[Artist] = ormar.ForeignKey(Artist, name="artist_id")
|
但目前您无法更改 ManyToMany 列名称,因为它们无论如何都会经过其他模型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 | import databases
import ormar
import sqlalchemy
DATABASE_URl = "sqlite:///test.db"
database = databases.Database(DATABASE_URl, force_rollback=True)
metadata = sqlalchemy.MetaData()
class Child(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
tablename="children",
)
id: int = ormar.Integer(name="child_id", primary_key=True)
first_name: str = ormar.String(name="fname", max_length=100)
last_name: str = ormar.String(name="lname", max_length=100)
born_year: int = ormar.Integer(name="year_born", nullable=True)
class ArtistChildren(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
tablename="children_x_artists",
)
class Artist(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
tablename="artists",
)
id: int = ormar.Integer(name="artist_id", primary_key=True)
first_name: str = ormar.String(name="fname", max_length=100)
last_name: str = ormar.String(name="lname", max_length=100)
born_year: int = ormar.Integer(name="year")
children = ormar.ManyToMany(Child, through=ArtistChildren)
|
覆盖默认的查询集
如果您想自定义 ormar 运行的查询,您可以在模型类中定义您自己的查询集类(扩展了 ormar QuerySet),默认的就是 QuerySet
您可以在类的 ormar_config 中提供一个新类作为 queryset_class 参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | import ormar
from ormar.queryset.queryset import QuerySet
from fastapi import HTTPException
class MyQuerySetClass(QuerySet):
async def first_or_404(self, *args, **kwargs):
entity = await self.get_or_none(*args, **kwargs)
if entity is None:
# in fastapi or starlette
raise HTTPException(404)
class Book(ormar.Model):
ormar_config = base_ormar_config.copy(
queryset_class=MyQuerySetClass,
tablename="book",
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=32)
# when book not found, raise `404` in your view.
book = await Book.objects.first_or_404(name="123")
|
类型提示
请注意,为了更好的 IDE 支持和 mypy 检查,您可以提供类型提示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
|
请注意,类型提示是可选的,因此完全有效的 ormar 代码可以如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id = ormar.Integer(primary_key=True)
name = ormar.String(max_length=100)
completed = ormar.Boolean(default=False)
|
!!!警告即使您使用类型提示,ormar 也不会使用它们来构造 pydantic 字段!
| Type hints are there only to support static checkers and linting,
`ormar` construct annotations used by `pydantic` from own fields.
|
依赖关系
由于 ormar 依赖于数据库和 sqlalchemy-core 来进行数据库连接和表创建,因此您需要为每个模型分配两个特殊参数。
数据库
一种是使用 sqlalchemy 连接字符串格式的数据库 URL 创建的数据库实例。
创建的实例需要使用 ormar_config 对象数据库参数传递给每个模型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
|
!!!提示 您只需创建一次数据库实例并将其用于所有模型。如果您想使用多个数据库,您可以创建多个数据库。
炼金术
第二个依赖项是 sqlalchemy MetaData 实例。
创建的实例需要使用 ormar_config 对象元数据参数传递给每个模型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
|
!!!提示 您只需创建一次 MetaData 实例并将其用于所有模型。如果您想使用多个数据库,您可以创建多个数据库。
最佳实践
请注意,ormar 需要名为 ormar_config 的字段,该字段是 OrmarConfig 类的实例。为了简化配置管理,OrmarConfig 类提供了复制方法。因此,您应该创建一个基础对象并在所有模型中使用其副本,而不是一遍又一遍地为所有模型提供相同的参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 | from typing import Optional
import databases
import ormar
import sqlalchemy
DATABASE_URL = "sqlite:///test.db"
ormar_base_config = ormar.OrmarConfig(
database=databases.Database(DATABASE_URL),
metadata=sqlalchemy.MetaData(),
)
class Artist(ormar.Model):
# note that tablename is optional
# if not provided ormar will user class.__name__.lower()+'s'
# -> artists in this example
ormar_config = ormar_base_config.copy()
id: int = ormar.Integer(primary_key=True)
first_name: str = ormar.String(max_length=100)
last_name: str = ormar.String(max_length=100)
born_year: int = ormar.Integer(name="year")
class Album(ormar.Model):
ormar_config = ormar_base_config.copy()
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
artist: Optional[Artist] = ormar.ForeignKey(Artist)
|
表名
默认情况下,表名称是根据模型类名称创建的,即小写名称加“s”。
您可以通过提供 ormar_config 对象的表名参数来覆盖此参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
# if you omit this parameter it will be created automatically
# as class.__name__.lower()+'s' -> "courses" in this example
tablename="my_courses",
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
|
约束条件
在模型级别,您还可以对 sql 列设置模型方面的约束。
目前仅支持 IndexColumns、UniqueColumns 和 CheckColumns 约束。
!!!note 请注意,仅当您要在约束上设置名称或要在多个列上设置索引时才应使用这两个约束,否则首选 ormar 字段上的索引和唯一属性。
!!!tip 要了解有关主键、唯一、外键等列约束的更多信息,请访问字段。
独特的栏目
您可以通过提供 ormar_config 对象约束参数来设置此参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
# define your constraints in OrmarConfig of the model
# it's a list that can contain multiple constraints
# hera a combination of name and column will have to be unique in db
constraints=[ormar.UniqueColumns("name", "completed")],
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
|
!!!note 请注意,约束是针对应该是唯一的列的组合。要将一列设置为唯一,请使用唯一的通用参数。当然,您可以使用此参数将许多列设置为唯一,但每个列都将单独检查。
索引列
您可以通过提供 ormar_config 对象约束参数来设置此参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
# define your constraints in OrmarConfig of the model
# it's a list that can contain multiple constraints
# hera a combination of name and column will have a compound index in the db
constraints=[ormar.IndexColumns("name", "completed")],
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
|
!!!note 请注意,约束是针对索引中应包含的列的组合。要设置一列索引,请使用唯一的公共参数。当然,您可以使用此参数将许多列设置为索引,但每个列都将是一个单独的索引。
检查列
您可以通过提供 ormar_config 对象约束参数来设置此参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | import datetime
import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
# define your constraints in OrmarConfig of the model
# it's a list that can contain multiple constraints
# hera a combination of name and column will have a level check in the db
constraints=[
ormar.CheckColumns("start_time < end_time", name="date_check"),
],
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
start_date: datetime.date = ormar.Date()
end_date: datetime.date = ormar.Date()
|
!!!note 请注意,某些数据库并不主动支持检查约束(例如 MySQL)。
悬臂结构
由于每个 ormar.Model 也是一个 pydantic 模型,您可能需要调整 pydantic 配置的设置。
在 pydantic 中执行此操作的方法是调整提供给模型的 model_config 字典上的设置,它对于 ormar 模型的工作原理完全相同。
因此,为了设置您自己的首选项,您不仅需要向模型提供 ormar_config 类,还需要提供 model_config = ConfigDict() 类。
!!!note 要了解有关可用设置的更多信息,请访问 pydantic 配置页面。
请注意,如果您不提供自己的配置,ormar 将为您完成。提供的默认配置如下:
| model_config = ConfigDict(validate_assignment=True, ser_json_bytes="base64")
|
因此,要覆盖设置或提供您自己的示例模型,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | import databases
import ormar
import pydantic
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
model_config = pydantic.ConfigDict(frozen=True)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
|
模型中的额外字段
默认情况下,ormar 禁止您将额外的字段传递给模型。
如果您尝试这样做,将会引发 ModelError。
由于额外的字段无法保存在数据库中,因此默认禁止此类字段似乎是一个可行的选择。
相反,在 pydantic 中,默认选项是忽略此类额外字段,因此 ormar 提供了 ormar_config.extra 设置以相同的方式运行。
要忽略传递给 ormar 的额外字段,请将此设置设置为 Extra.ignore 而不是默认的 Extra.forbid。
请注意,ormar 不允许接受额外字段,您只能忽略它们或禁止它们(如果存在则引发异常)
| from ormar import Extra, OrmarConfig
class Child(ormar.Model):
ormar_config = OrmarConfig(
tablename="children",
extra=Extra.ignore # set extra setting to prevent exceptions on extra fields presence
)
id: int = ormar.Integer(name="child_id", primary_key=True)
first_name: str = ormar.String(name="fname", max_length=100)
last_name: str = ormar.String(name="lname", max_length=100)
|
要在所有模型上设置相同的设置,请检查最佳实践和 base_ormar_config 概念。
型号排序
默认情况下,使用给定模型查询数据库时,模型按 Primary_key 列升序排序。如果您希望更改默认行为,可以通过向模型或mar_config 对象提供orders_by 参数来实现。
默认排序示例(未指定 - 因此由主键指定):
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | base_ormar_config = ormar.OrmarConfig(
database=databases.Database(DATABASE_URL),
metadata=sqlalchemy.MetaData(),
)
# default sort by column id ascending
class Author(ormar.Model):
ormar_config = base_ormar_config.copy(
tablename="authors",
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
|
修改的
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | base_ormar_config = ormar.OrmarConfig(
database=databases.Database(DATABASE_URL),
metadata=sqlalchemy.MetaData(),
)
# now default sort by name descending
class Author(ormar.Model):
ormar_config = base_ormar_config.copy(
orders_by = ["-name"],
tablename="authors",
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
|
模型初始化
有两种方法可以在数据库中创建和保存模型实例。
如果您计划在稍后执行程序时修改实例,您可以将模型作为普通类启动,然后等待 save() 调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | import asyncio
import databases
import ormar
import sqlalchemy
from examples import create_drop_database
DATABASE_URL = "sqlite:///test.db"
ormar_base_config = ormar.OrmarConfig(
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
)
class Course(ormar.Model):
ormar_config = ormar_base_config.copy(tablename="courses")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
@create_drop_database(base_config=ormar_base_config)
async def run_query():
course = Course(name="Painting for dummies", completed=False)
await course.save()
await Course.objects.create(name="Painting for dummies", completed=False)
asyncio.run(run_query())
|
如果您想启动模型并同时保存在数据库中,请使用 QuerySet 的方法 create()。
为了一次创建多个对象,可以使用bulk_create() QuerySet 的方法。
每个模型都有一个初始化为对象参数的查询集
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | import asyncio
import databases
import ormar
import sqlalchemy
from examples import create_drop_database
DATABASE_URL = "sqlite:///test.db"
ormar_base_config = ormar.OrmarConfig(
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
)
class Course(ormar.Model):
ormar_config = ormar_base_config.copy(tablename="courses")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
@create_drop_database(base_config=ormar_base_config)
async def run_query():
course = Course(name="Painting for dummies", completed=False)
await course.save()
await Course.objects.create(name="Painting for dummies", completed=False)
asyncio.run(run_query())
|
!!!info 要了解有关查询集(包括批量操作)和可用方法的更多信息,请访问查询
模型保存状态
每个模型实例都是一个单独的 python 对象,它们彼此不了解任何信息。
| track1 = await Track.objects.get(name='The Bird')
track2 = await Track.objects.get(name='The Bird')
assert track1 == track2 # True
track1.name = 'The Bird2'
await track1.save()
assert track1.name == track2.name # False
# track2 does not update and knows nothing about track1
|
对象本身有一个保存状态,设置如下:
- 在模型上保存/更新/加载/更新插入方法后保存模型
- 模型保存后
create/get/first/all/get_or_create/update_or_create
方法
- 模型在传递给bulk_update和bulk_create时被保存
- 添加/删除ManyToMany相关对象后保存模型(通过模型实例自动保存/删除)
- 更改任何自己的字段后不会保存模型(包括作为 Model.pk 别名的 pk)
- 添加/删除ForeignKey相关对象后模型未保存(fk列未保存)
- 使用 __init__ 实例化后不保存模型(不使用 QuerySet.create 或调用 save 之前)
您可以使用 ModelInstance.saved 属性检查模型是否已保存