Django - 模型
- 模型准确且唯一的描述了数据。它包含您储存的数据的重要字段和行为。一般来说,每一个模型都映射一张数据库表。
- 每个模型都是一个 Python 的类,这些类继承
django.db.models.Model
- 模型类的每个属性都相当于一个数据库的字段。
- 利用这些,Django 提供了一个自动生成访问数据库的 API;请参阅 执行查询。
配置数据库
- Django 支持 MySQL 5.7 及以上版本。
- 配置 MySQL 数据库
1
2
3
4
5
6
7
8
9
10DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "db_name",
"HOST": "host",
"PORT": 3306,
"USER": "user",
"PASSWORD": "password"
}
}
配置语言与时区
1 | LANGUAGE_CODE = "zh-hans" |
设置
USE_TZ = False
,以保证表中字段的创建时间和本地时间一致。
- 一旦你定义了你的模型,你需要告诉 Django 你准备 使用 这些模型。你需要修改设置文件中的
INSTALLED_APPS
,在这个设置中添加包含models.py
文件的模块名称。 - 例如,若模型位于项目中的
user.models
模块( 此包结构由manage.py startapp
命令创建),INSTALLED_APPS
应设置如下:1
2
3
4
5INSTALLED_APPS = [
#...
'user',
#...
] - 当你向
INSTALLED_APPS
添加新的应用的时候,请务必运行manage.py migrate
,此外你也可以先使用以下命令进行迁移manage.py makemigrations
。1
2
3
4
5迁移:只是在当前app中的migrations文件夹中产生了一个类似0001_initial.py的文件
python .\manage.py makemigrations
同步:同步模型,生成数据表
python .\manage.py migrate
字段
-
字段概述
模型中最重要且唯一必要的是数据库的字段定义。字段在类属性中定义。定义字段名时应小心避免使用与 模型 API 冲突的名称, 如
clean
,save
,ordelete
等。 -
字段选项
-
null
- 如果是
True
, Django 将在数据库中存储空值为NULL
。默认为False
。 - 免在基于字符串的字段上使用
null
,如CharField
和TextField
。如果一个基于字符串的字段有null=True
,这意味着它有两种可能的“无数据”值。NULL
,和空字符串。在大多数情况下,“无数据”有两种可能的值是多余的,Django 的惯例是使用空字符串,而不是NULL
。一个例外是当一个CharField
同时设置了unique=True
和blank=True
。在这种情况下,null=True
是需要的,以避免在保存具有空白值的多个对象时违反唯一约束。 - 无论是基于字符串的字段还是非字符串的字段,如果希望在表单中允许空值,还需要设置
blank=True
,因为null
参数只影响数据库的存储(参见blank
)。
- 如果是
-
blank
-
choices
-
db_column
- 这个字段要使用的数据库列名。如果没有给出列名,Django 将使用字段名。
- 如果你的数据库列名是 SQL 的保留字,或者包含了 Python 变量名中不允许的字符——特别是连字符——那也没关系。Django 会在幕后引用列名和表名。
-
db_index
如果是
True
,将为该字段创建数据库索引。 -
db_tablespace
如果这个字段有索引,那么要为这个字段的索引使用的 数据库表空间 的名称。默认是项目的
DEFAULT_INDEX_TABLESPACE
设置(如果有设置),或者是模型的db_tablespace
(如果有)。如果后端不支持索引的表空间,则忽略此选项。 -
default
- 该字段的默认值。可以是一个值或者是个可调用的对象,如果是个可调用对象,每次实例化模型时都会调用该对象。
- 默认值不能是一个可更改的对象(模型实例、
list
、set
等),因为对该对象同一实例的引用将被用作所有新模型实例的缺省值。相反,将所需的默认值包裹在一个可调用对象中。
-
editable
如果是
False
,该字段将不会在管理或任何其他ModelForm
中显示。在 模型验证 中也会跳过。默认为True
。 -
error_messages
error_messages
参数可以让你覆盖该字段引发的默认消息。传入一个与你想覆盖的错误信息相匹配的键值的字典。- 错误信息键包括
null
、blank
、invalid
、invalid_choice
、unique
和unique_for_date
。在下面的 字段类型 一节中为每个字段指定了额外的错误信息键。 - 这些错误信息通常不会传播到表单中。参见 有关模型的 error_messages 的注意事项。
-
help_text
- 额外的“帮助”文本,随表单控件一同显示。即便你的字段未用于表单,它对于生成文档也是很有用的。
- 请注意,在自动生成的表格中,这个值 不是 HTML 转义的。如果你愿意的话,你可以在
help_text
中加入 HTML。例如:1
help_text="Please use the following format: <em>YYYY-MM-DD</em>."
- 或者你可以使用纯文本和
django.utils.html.escape()
来转义任何 HTML 特殊字符。确保你转义任何可能来自不受信任的用户的帮助文本,以避免跨站脚本攻击。
-
primary_key
- 如果设置为
True
,将该字段设置为该模型的主键。 - 如果你没有为模型中的任何字段指定
primary_key=True
,Django 会自动添加一个字段来保存主键,所以你不需要在任何字段上设置 primary_key=True,除非你想覆盖默认主键行为。自动创建的主键字段的类型可以在AppConfig.default_auto_field
中为每个应用程序指定,或者在DEFAULT_AUTO_FIELD
配置中全局指定。更多信息,请参阅 自动设置主键。 primary_key=True
意味着null=False
和unique=True
。一个对象只允许有一个主键。- 主键字段是只读的。如果您改变了现有对象的主键值,然后将其保存,则会在旧对象旁边创建一个新对象。
- 如果设置为
-
unique
- 如果设置为
True
,这个字段必须在整个表中保持值唯一。 - 这是在数据库级别和模型验证中强制执行的。如果你试图保存一个在
unique
字段中存在重复值的模型,模型的save()
方法将引发django.db.IntegrityError
。 - 除了
ManyToManyField
和OneToOneField
之外,该选项对所有字段类型有效。 - 请注意,当
unique
为True
时,你不需要指定db_index
,因为unique
意味着创建一个索引。
- 如果设置为
-
unique_for_date
- 将其设置为
DateField
或DateTimeField
的名称,要求该字段的日期字段值是唯一的。 - 例如,如果你的字段
title
有unique_for_date="pub_date"
,那么 Django 就不允许输入两条相同title
和pub_date
的记录。 - 请注意,如果将其设置为指向
DateTimeField
,则只考虑该字段的日期部分。此外,当USE_TZ
为True
时,检查将在对象保存时的 当前时区 中进行。 - 这在模型验证过程中由
Model.validate_unique()
强制执行,但在数据库级别上不执行。如果任何unique_for_date
约束涉及的字段不属于ModelForm
(例如,如果其中一个字段被列在exclude
中,或者有editable=False
),Model.validate_unique()
将跳过对该特定约束的验证。
- 将其设置为
-
unique_for_date
像
unique_for_date
一样,但要求字段对月份是唯一的。 -
unique_for_year
如
unique_fordate
和unique_formonth
。 -
verbose_name
字段的一个人类可读名称,如果没有给定详细名称,Django 会使用字段的属性名自动创建,并将下划线转换为空格。参见 详细字段名。
-
validators
要为该字段运行的验证器列表。更多信息请参见 验证器文档。
-
-
字段类型
-
AutoField
一个
IntegerField
,根据可用的 ID 自动递增。你通常不需要直接使用它;如果你没有指定,主键字段会自动添加到你的模型中。参见 自动设置主键。 -
BigAutoField
一个 64 位整数,与
AutoField
很相似,但保证适合1
到9223372036854775807
的数字。 -
BigIntegerField
一个 64 位的整数,和
IntegerField
很像,只是它保证适合从-9223372036854775808
到9223372036854775807
的数字。该字段的默认表单部件是一个NumberInput
。 -
BinaryField
- 一个用于存储原始二进制数据的字段。可以指定为
bytes
、bytearray
或memoryview
。 - 默认情况下,
BinaryField
将ediditable
设置为False
,在这种情况下,它不能被包含在ModelForm
中。
- 一个用于存储原始二进制数据的字段。可以指定为
-
BooleanField
- 一个 true/false 字段。
- 该字段的默认表单部件是
CheckboxInput
,或者如果null=True
则是NullBooleanSelect
。 - 当
Field.default
没有定义时,BooleanField
的默认值是None
。
-
CharField
- 一个字符串字段,适用于小到大的字符串。
- 对于大量的文本,使用
TextField
。 - 该字段的默认表单部件是一个
TextInput
。 CharField.max_length
必须的。该字段的最大长度(以字符为单位)。max_length 在数据库层面强制执行,在 Django 的验证中使用MaxLengthValidator
。CharField.db_collation
可选的。该字段的数据库字符序名称。
-
DateField
- 一个日期,在 Python 中用一个
datetime.date
实例表示。有一些额外的、可选的参数。 DateField.auto_now
- 每次保存对象时,自动将该字段设置为现在。对于“最后修改”的时间戳很有用。请注意,当前日期 总是 被使用,而不仅仅是一个你可以覆盖的默认值。
- 只有在调用
Model.save()
时,该字段才会自动更新。当以其他方式对其他字段进行更新时,如QuerySet.update()
,该字段不会被更新,尽管你可以在这样的更新中为该字段指定一个自定义值。
DateField.auto_now_add
- 当第一次创建对象时,自动将该字段设置为现在。对创建时间戳很有用。请注意,当前日期是 始终 使用的;它不是一个你可以覆盖的默认值。因此,即使你在创建对象时为该字段设置了一个值,它也会被忽略。如果你想修改这个字段,可以设置以下内容来代替
auto_now_add=True
:- 对于
DateField
:default=date.today
——来自datetime.date.today()
- 对于
DateTimeField
:default=timezone.now
——来自django.utils.timezone.now()
- 对于
- 该字段的默认表单部件是一个
DateInput
。管理中增加了一个 JavaScript 日历,以及“今天”的快捷方式。包含一个额外的invalid_date
错误信息键。 auto_now_add
、auto_now
和default
选项是相互排斥的。这些选项的任何组合都会导致错误。
- 当第一次创建对象时,自动将该字段设置为现在。对创建时间戳很有用。请注意,当前日期是 始终 使用的;它不是一个你可以覆盖的默认值。因此,即使你在创建对象时为该字段设置了一个值,它也会被忽略。如果你想修改这个字段,可以设置以下内容来代替
- 一个日期,在 Python 中用一个
-
DateTimeField
- 一个日期和时间,在 Python 中用一个
datetime.datetime
实例表示。与DateField
一样,使用相同的额外参数。 - 该字段的默认表单部件是一个单独的
DateTimeInput
。管理中使用两个单独的TextInput
部件,并使用 JavaScript 快捷方式。
- 一个日期和时间,在 Python 中用一个
-
DecimalField
- 一个固定精度的十进制数,在 Python 中用一个
Decimal
实例来表示。它使用DecimalValidator
验证输入。 - 具有以下必需参数:
DecimalField.max_digits
数字中允许的最大位数。请注意,这个数字必须大于或等于decimal_places
。DecimalField.decimal_places
与数字一起存储的小数位数。
- 例如,要存储最高为
999.99
的数字,精度为小数点后 2 位,你可以使用:1
models.DecimalField(..., max_digits=5, decimal_places=2)
- 一个固定精度的十进制数,在 Python 中用一个
-
DurationField
一个用于存储时间段的字段 —— 在 Python 中用
timedelta
建模。当在 PostgreSQL 上使用时,使用的数据类型是interval
,在 Oracle 上使用的数据类型是INTERVAL DAY(9) TO SECOND(6)
。否则使用微秒的bigint
。 -
EmailField
一个
CharField
,使用EmailValidator
来检查该值是否为有效的电子邮件地址。 -
FileField
- 一个文件上传字段
primary_key
参数不支持,如果使用,会引起错误。- 具有以下可选参数:
FileField.upload_to
- 这个属性提供了一种设置上传目录和文件名的方式,可以有两种设置方式。在这两种情况下,值都会传递给
Storage.save()
方法。 - 如果你指定一个字符串值或一个
Path
,它可能包含strftime()
格式,它将被文件上传的日期/时间所代替(这样上传的文件就不会填满指定的目录)。 - 如果你使用的是默认的
FileSystemStorage
,这个字符串的值将被附加到你的MEDIA_ROOT
路径后面,形成本地文件系统中上传文件的存储位置。如果你使用的是不同的存储系统,请检查该存储系统的文档,看看它是如何处理upload_to
的。 upload_to
也可以是一个可调用对象,如函数。这个函数将被调用以获得上传路径,包括文件名。这个可调用对象必须接受两个参数,并返回一个 Unix 风格的路径(带斜线),以便传给存储系统。参数 描述 instance
定义 FileField
的模型实例。更具体地说,这是附加当前文件的特定实例。在大多数情况下,这个对象还没有被保存到数据库,所以如果它使用默认的AutoField
,它的主键字段可能还没有一个值。filename
最初给文件的文件名。在确定最终目标路径时,可能会考虑到,也可能不会考虑到。
- 这个属性提供了一种设置上传目录和文件名的方式,可以有两种设置方式。在这两种情况下,值都会传递给
FileField.storage
一个存储对象,或是一个返回存储对象的可调用对象。它处理你的文件的存储和检索。参见 管理文件,了解如何提供这个对象。
- 该字段的默认表单部件是一个
ClearableFileInput
。 - 在模型中使用
FileField
或ImageField
(见下文)需要几个步骤:- 在你的配置文件中,你需要将
MEDIA_ROOT
定义为你希望 Django 存储上传文件的目录的完整路径。(为了提高性能,这些文件不会存储在数据库中。)将MEDIA_URL
定义为该目录的基本公共 URL。确保这个目录是可以被网络服务器的用户账户写入的。 - 将
FileField
或ImageField
添加到你的模型中,定义upload_to
选项,指定MEDIA_ROOT
的子目录,用于上传文件。 - 所有这些将被存储在你的数据库中的是一个文件的路径(相对于
MEDIA_ROOT
)。你很可能要使用 Django 提供的方便的url
属性。例如,如果你的ImageField
叫做mug_shot
,你可以在模板中使用{{ object.mug_shot.url }}
获取图片的绝对路径。
- 在你的配置文件中,你需要将
- 如果你想检索上传文件的盘上文件名,或者文件的大小,可以分别使用
name
和size
属性;关于可用属性和方法的更多信息,请参见File
类参考和 管理文件 主题指南。
-
FilePathField
-
一个
CharField
,其选择仅限于文件系统中某个目录下的文件名。有一些特殊的参数,其中第一个参数是 必须的。 -
FilePathField.path
- 必须的。一个目录的绝对文件系统路径,这个
FilePathField
应从该目录中获取其选择。例如:"/home/images"
。 path
也可以是一个可调用对象,可以是在运行时动态设置路径的函数。
- 必须的。一个目录的绝对文件系统路径,这个
-
FilePathField.match
- 可选。一个正则表达式,作为一个字符串,
FilePathField
将用于过滤文件名。请注意,正则表达式将被应用于基本文件名,而不是完整的路径。例如:"foo.*.txt$"
,它将匹配名为foo23.txt
的文件,但不匹配bar.txt
或foo23.png
。 - 一个潜在的问题是
match
适用于基本文件名,而不是完整的路径。
- 可选。一个正则表达式,作为一个字符串,
-
FilePathField.recursive
可选。
True
或False
。默认为False
。指定是否包含path
的所有子目录。 -
FilePathField.allow_files
可选。
True
或False
。 默认值是True
。 指定是否应该包含指定位置的文件。 这个或allow_folders
必须是True
。 -
FilePathField.allow_folders
可选。
True
或False
。 默认为False
。 指定是否应该包含指定位置的文件夹。 这个或allow_files
必须是True
。 -
FilePathField
实例在数据库中作为varchar
列创建,默认最大长度为 100 个字符。与其他字段一样,你可以使用max_length
参数改变最大长度。
-
-
FloatField
- 在 Python 中用一个
float
实例表示的浮点数。 - 当
localize
为False
时是NumberInput
否则,该字段的默认表单部件是TextInput
。
- 在 Python 中用一个
-
GenericIPAddressField
-
IPv4 或 IPv6 地址,字符串格式(如
192.0.2.30
或2a02:42fe::4
)。该字段的默认表单部件是一个TextInput
。 -
IPv6 地址规范化遵循 RFC 4291#section-2.2 第 2.2 节,包括使用该节第 3 段建议的 IPv4 格式,如
::fffff:192.0.2.0
。例如,2001:0::0:01
将被标准化为2001::1
,::fffff:0a0a:0a0a
将被标准化为::fffff:10.10.10.10
。所有字符都转换为小写。 -
GenericIPAddressField.protocol
将有效输入限制为指定协议。接受的值是
'both'
(默认)、'IPv4'
或'IPv6'
。匹配是不分大小写的。 -
GenericIPAddressField.unpack_ipv4
解压 IPv4 映射地址,如
::fffff:192.0.2.1
。如果启用该选项,该地址将被解压为192.0.2.1
。默认为禁用。只有当protocol
设置为'both'
时才会启用。 -
如果允许空值,就必须允许 null 值,因为空值会被存储为 null。
-
-
ImageField
- 继承
FileField
的所有属性和方法,但也验证上传的对象是有效的图像。 - 除了
FileField
的特殊属性外,ImageField
也有height
和width
属性。 - 为了方便查询这些属性,
ImageField
有以下可选参数:ImageField.height_field
模型字段的名称,每次保存模型实例时将自动填充图像的高度。ImageField.width_field
模型字段的名称,每次保存模型实例时将自动填充图像的宽度。
- 需要 Pillow 库。
ImageField
实例在数据库中创建为varchar
列,默认最大长度为 100 个字符。与其他字段一样,你可以使用max_length
参数改变最大长度。- 该字段的默认表单部件是一个
ClearableFileInput
。
- 继承
-
IntegerField
- 一个整数。从
-2147483648
到2147483647
的值在 Django 支持的所有数据库中都是安全的。 - 它使用
MinValueValidator
和MaxValueValidator
根据默认数据库支持的值来验证输入。 - 当
localize
为False
时是NumberInput
否则,该字段的默认表单部件是TextInput
。
- 一个整数。从
-
JSONField
- 一个用于存储 JSON 编码数据的字段。在 Python 中,数据以其 Python 本地格式表示:字典、列表、字符串、数字、布尔值和
None
。 - JSONField 支持 MariaDB、MySQL 5.7.8+、Oracle、PostgreSQL 和 SQLite(启用了 JSON1 扩展)。
JSONField.encoder
- 一个可选的
json.JSONEncoder
子类,用于序列化标准 JSON 序列化器不支持的数据类型(例如datetime.datetime
或UUID
)。例如,你可以使用DjangoJSONEncoder
类。 - 默认为
json.JSONEncoder
。
- 一个可选的
JSONField.decoder
- 一个可选的
json.JSONDecoder
子类,用于反序列化从数据库中获取的值。该值将采用自定义编码器选择的格式(通常是字符串)。你的反序列化可能需要考虑到你无法确定输入类型的事实。例如,你有可能返回一个datetime
,实际上是一个字符串,而这个字符串恰好与datetime
选择的格式相同。 - 默认为
json.JSONDecoder
。
- 一个可选的
- 如果你给字段一个
default
,确保它是一个不可变的对象,比如str
,或者是一个每次返回一个新的可变对象的可调用对象,比如dict
或一个函数。提供一个像default={}
或default=[]
这样的可改变的默认对象,在所有模型实例之间共享一个对象。 - 要在数据库中查询
JSONField
,请看 查询 JSONField。
- 一个用于存储 JSON 编码数据的字段。在 Python 中,数据以其 Python 本地格式表示:字典、列表、字符串、数字、布尔值和
-
PositiveBigIntegerField
就像一个
PositiveIntegerField
,但只允许在某一特定点下的值(依赖于数据库)。0
到9223372036854775807
的值在 Django 支持的所有数据库中都是安全的。 -
PositiveIntegerField
就像
IntegerField
一样,但必须是正值或零(0
)。从0
到2147483647
的值在 Django 支持的所有数据库中都是安全的。出于向后兼容的原因,接受0
的值。 -
PositiveSmallIntegerField
就像一个
PositiveIntegerField
,但只允许在某一特定(数据库依赖的)点下取值。0
到32767
的值在 Django 支持的所有数据库中都是安全的。 -
SlugField
- Slug 是一个报纸术语。slug 是一个简短的标签,只包含字母、数字、下划线或连字符。它们一般用于 URL 中。
- 像
CharField
一样,你可以指定max_length
(也请阅读那一节中关于数据库可移植性和max_length
的说明)。如果没有指定max_length
,Django 将使用默认长度 50。 - 意味着将
Field.db_index
设置为True
。 - 基于其他值的值自动预填充一个 SlugField 通常是很有用的。 你可以在管理中使用
prepopulated_fields
来自动完成。 - 它使用
validate_slug
或validate_unicode_slug
进行验证。 SlugField.allow_unicode
如果是True
,该字段除了接受 ASCII 字母外,还接受 Unicode 字母。默认值为False
。
-
SmallAutoField
就像一个
AutoField
,但只允许值在一定(依赖于数据库)的限制下。1
到32767
的值在 Django 支持的所有数据库中都是安全的。 -
SmallAutoField
就像一个
IntegerField
,但只允许在某一特定(依赖于数据库的)点下取值。从-32768
到32767
的值在 Django 支持的所有数据库中都是安全的。 -
TextField
-
TimeField
-
URLField
- URL 的
CharField
,由URLValidator
验证。 - 该字段的默认表单部件是一个
URLInput
。 - 像所有的
CharField
子类一样,URLField
接受可选的max_length
参数。如果你没有指定max_length
参数,则使用默认的 200。
- URL 的
-
UUIDField
- 一个用于存储通用唯一标识符的字段。使用 Python 的
UUID
类。当在 PostgreSQL 上使用时,它存储在一个uuid
的数据类型中,否则存储在一个char(32)
中。 - 通用唯一标识符是
primary_key
的AutoField
的一个很好的替代方案。数据库不会为你生成 UUID,所以建议使用default
:1
2
3
4
5
6import uuid
from django.db import models
class MyUUIDModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# other fields - 请注意,一个可调用对象(省略括号)被传递给
default
,而不是UUID
的实例。
- 一个用于存储通用唯一标识符的字段。使用 Python 的
-
-
可用的
Meta
选项-
abstract
如果
abstract = True
,这个模型将是一个 抽象基类,模型类作为父类需要添加。 -
app_label
- 如果在
INSTALLED_APPS
中定义了一个应用程序之外的模型,它必须声明它属于哪个应用程序:1
app_label = 'myapp'
- 如果你想用
app_label.object_name
或app_label.model_name
来表示一个模型,你可以分别使用model._meta.label
或model._meta.label_lower
。
- 如果在
-
base_manager_name
管理器的属性名,例如,
'objects'
,用于模型的_base_manager
。 -
db_table
- 用于模型的数据库表的名称:
1
db_table = 'music_album'
- 表名称
- 为了节省你的时间,Django 会自动从你的模型类和包含它的应用程序的名称中导出数据库表的名称。一个模型的数据库表名是通过将模型的“app label” —— 你在
manage.py startapp
中使用的名称 —— 与模型的类名连接起来,并在两者之间加上下划线。 - 例如,如果你有一个应用程序
bookstore
(由manage.py startapp bookstore
创建),一个定义为class Book
的模型将有一个名为bookstore_book
的数据库表。 - 要覆盖数据库表名,使用
class Meta
中的db_table
参数。 - 如果你的数据库表名是 SQL 的保留字,或者包含 Python 变量名中不允许的字符 —— 特别是连字符 —— 那也没关系。Django 会在幕后引用列名和表名。
- 为了节省你的时间,Django 会自动从你的模型类和包含它的应用程序的名称中导出数据库表的名称。一个模型的数据库表名是通过将模型的“app label” —— 你在
- 用于模型的数据库表的名称:
-
db_tablespace
模型要使用的 数据库表空间 名称。如果有设置的话,默认是项目的
DEFAULT_TABLESPACE
配置。如果后端不支持表空间,则忽略此选项。 -
default_manager_name
模型的
_default_manager
管理器名称。 -
default_related_name
- 从相关对象到这个对象的关系默认使用的名称。默认为
_set
。 - 这个选项还可以设置
related_query_name
。 - 由于字段的反向名称应该是唯一的,所以如果你打算对你的模型进行子类化,就要小心了。为了避免名称冲突,名称的一部分应该包含
'%(app_label)s'
和'%(model_name)s'
,它们分别被模型所在的应用程序的名称和模型的名称所取代,都是小写的。见 抽象模型的相关名称 段落。
- 从相关对象到这个对象的关系默认使用的名称。默认为
-
get_latest_by
- 模型中的字段名或字段名列表,通常是
DateField
,DateTimeField
或IntegerField
。这指定了在你的模型中使用的默认字段Manager
的last()
和earliest()
方法。例如:1
2
3
4
5# Latest by ascending order_date.
get_latest_by = "order_date"
# Latest by priority descending, order_date ascending.
get_latest_by = ['-priority', 'order_date'] - 更多内容请参见
last()
文档。
- 模型中的字段名或字段名列表,通常是
-
managed
- 默认为
True
,意味着 Django 会在migrate
中创建相应的数据库表,或者作为迁移的一部分,并作为flush
管理命令的一部分删除它们。也就是说,Django 管理 数据库表的生命周期。 - 如果
False
,将不对该模型进行数据库表的创建、修改或删除操作。如果该模型代表一个现有的表或一个通过其他方式创建的数据库视图,这一点很有用。这是在managed=False
时 唯一 的区别。模型处理的所有其他方面都与正常情况完全相同。这包括- 如果不声明的话,在模型中增加一个自动主键字段。 为了避免给后面来的代码读者带来困惑,建议在使用非托管模型时,指定你所建模的数据库表的所有列。
- 如果一个带有
managed=False
的模型包含一个ManyToManyField
指向另一个非托管模型,那么多对多连接的中间表也不会被创建。但是,一个托管模型和一个非托管模型之间的中间表会被创建。 - 如果你需要改变这种默认行为,请将中间表创建为显式模型(根据需要设置
managed
),并使用ManyToManyField.through
属性让关系使用你的自定义模型。
- 对于涉及
managed=False
模型的测试,你要确保创建正确的表作为测试设置的一部分。 - 如果你对改变模型类的 Python 级行为感兴趣,你可以使用
managed=False
并创建一个现有模型的副本。然而,对于这种情况,有一个更好的方法: 代理模型。
- 默认为
-
order_with_respect_to
- 使该对象可以根据给定字段(通常是
ForeignKey
)进行排序。这可以用来使相关对象相对于父对象可排序。例如,如果一个Answer
与一个Question
对象相关,而一个问题有多个答案,并且答案的顺序很重要,你可以这样做:1
2
3
4
5
6
7
8
9
10
11
12from django.db import models
class Question(models.Model):
text = models.TextField()
# ...
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
# ...
class Meta:
order_with_respect_to = 'question' - 当设置了
order_with_respect_to
时,还提供了两个额外的方法来检索和设置相关对象的顺序:get_RELATED_order()
和set_RELATED_order()
。
- 使该对象可以根据给定字段(通常是
-
ordering
- 对象的默认排序,用于获取对象列表时:
1
2
3
4
5
6# 按 pub_date 字段升序排列
ordering = ['pub_date']
# 按 pub_date 降序排列
ordering = ['-pub_date']
# 先按 pub_date 降序,然后再按 author 升序
ordering = ['-pub_date', 'author'] - 这是一个字符串和/或查询表达式的元组或列表。每一个字符串都是一个字段名,前面有一个可选的
-
字头,表示降序。没有前缀-
的字段将按升序排列。使用字符串?
来随机排序。
- 对象的默认排序,用于获取对象列表时:
-
permissions
- 创建此对象时要输入权限表的额外权限。为每个模型自动创建添加、更改、删除和查看权限。这个例子指定了一个额外的权限,
can_deliver_pizzas
:1
permissions = [('can_deliver_pizzas', 'Can deliver pizzas')]
- 这是一个由二元元组组成的列表或元组,格式为
(permission_code, human_readable_permission_name)
。
- 创建此对象时要输入权限表的额外权限。为每个模型自动创建添加、更改、删除和查看权限。这个例子指定了一个额外的权限,
-
default_permissions
默认值为
('add', 'change', 'delete', 'view')
。你可以自定义这个列表,例如,如果你的应用不需要任何默认的权限,可以将其设置为空列表。它必须在模型创建之前通过migrate
在模型上指定,以防止任何遗漏的权限被创建。 -
proxy
如果
proxy = True
,作为另一个模型子类的模型将被视为 代理模型。 -
required_db_features
当前连接应具备的数据库特征列表,以便在迁移阶段考虑模型。例如,如果你将此列表设置为
['gis_enabled']
,则模型将只在支持 GIS 的数据库上同步。在使用多个数据库后端进行测试时,跳过一些模型也很有用。避免模型之间的关系,这些模型可能会被创建,也可能不会被创建,因为 ORM 不会处理这个问题。 -
required_db_vendor
本模型所特有的支持的数据库厂商名称。目前的内置厂商名称是:
sqlite
、postgresql
、mysql
和oracle
。如果该属性不为空,且当前连接厂商与之不匹配,则该模型将不会同步。 -
select_on_save
- 确定 Django 是否会使用 1.6 之前的
django.db.models.Model.save()
算法。旧的算法使用SELECT
来确定是否有一条现有的记录需要更新。新算法直接尝试UPDATE
。在一些罕见的情况下,Django 看不到现有行的UPDATE
。例如 PostgreSQL 的ON UPDATE
触发器会返回NULL
。在这种情况下,即使数据库中存在一条记录,新算法最终也会进行INSERT
。 - 通常不需要设置这个属性。默认值是
False
。 - 关于新旧保存算法,请参见
django.db.models.Model.save()
。
- 确定 Django 是否会使用 1.6 之前的
-
indexes
你想在模型上定义的 indexes 的列表:
1
2
3
4
5
6
7
8
9
10
11from django.db import models
class Customer(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
class Meta:
indexes = [
models.Index(fields=['last_name', 'first_name']),
models.Index(fields=['first_name'], name='first_name_idx'),
] -
unique_together
- 一组字段名,合起来必须是唯一的:
1
unique_together = [['driver', 'restaurant']]
- 这是一个列表,这些列表在一起考虑时必须是唯一的。它在Django 管理中使用,并在数据库级别执行(即在
CREATE TABLE
语句中包含适当的UNIQUE
语句)。 - 为方便起见,
unique_together
在处理单组字段时可以是一个单一的列表:1
unique_together = ['driver', 'restaurant']
- 一个
ManyToManyField
不能被包含在 unique_together 中。(不清楚那意味着什么!)如果你需要验证与ManyToManyField
相关的唯一性,可以尝试使用信号或显式through
模型。 - 在模型验证过程中,当约束条件被违反时引发的
ValidationError
具有unique_together
错误代码。
- 一组字段名,合起来必须是唯一的:
-
index_together
- 一组字段名,合在一起,是有索引的:
1
2
3index_together = [
["pub_date", "deadline"],
] - 该字段清单将被编入索引(即发出适当的
CREATE INDEX
语句)。 - 为方便起见,
index_together
在处理一组字段时,可以是一个单一的列表:1
index_together = ["pub_date", "deadline"]
- 一组字段名,合在一起,是有索引的:
-
constraints
你想在模型上定义的 约束 列表:
1
2
3
4
5
6
7
8
9from django.db import models
class Customer(models.Model):
age = models.IntegerField()
class Meta:
constraints = [
models.CheckConstraint(check=models.Q(age__gte=18), name='age_gte_18'),
] -
verbose_name
- 对象的可读名称,单数:
1
verbose_name = "pizza"
- 如果没有给定,Django 将使用一个 munged 版本的类名:
CamelCase
变成camel case
。
- 对象的可读名称,单数:
-
verbose_name_plural
- 对象的复数名称:
1
verbose_name_plural = "stories"
- 如果没有给定,Django 将使用
verbose_name
+"s"
。
- 对象的复数名称:
-
-
只读的
Meta
属性-
label
对象的表示,返回
app_label.object_name
,例如'polls.Question'
。 -
label_lower
模型的表示,返回
app_label.model_name
,例如'polls.question'
。
-
-
增加
-
方式一
1
2
3
4
5
6
7user = User()
user.username = username
user.password = hashlib.sha256(password.encode('utf-8')).hexdigest()
if phone:
user.phone = phone
user.save()
return HttpResponse('用户注册成功!') -
方式二
1
2
3
4password = hashlib.sha256(password.encode('utf-8')).hexdigest()
user = User.objects.create(username=username, password=password, phone=phone)
if user:
return HttpResponse('用户注册成功!')
-
-
删除
- 找到要删除的对象
- 该对象去调用
delete()
1
2
3
4
5
6
7
8
9
10user = User.objects.get(pk=1)
if user:
# 删除:逻辑删除
user.is_delete = 1
user.save()
# 删除:物理删除
result = user.delete()
print(result)
# (1, {'user.User': 1})
# 第一个1表示受影响的行数(删除的行数),第二个1表示被删除的id
-
修改
-
方式一
1
2
3
4
5
6
7
8
9
10
11
12
13# 获取需要更新的数据
id = request.POST.get('id')
username = request.POST.get('username')
phone = request.POST.get('phone')
is_delete = True if request.POST.get('is_delete') else False
# 查找更新的是哪个用户
user = User.objects.get(pk=id)
# 更新数据
user.username = username
user.phone = phone
user.is_delete = is_delete
user.save()
return redirect('user:show') -
方式二
1
2
3
4
5
6
7
8
9# 获取需要更新的数据
id = request.POST.get('id')
username = request.POST.get('username')
phone = request.POST.get('phone')
is_delete = True if request.POST.get('is_delete') else False
# 更新数据
result = User.objects.filter(id=id).update(username=username, phone=phone, is_delete=is_delete)
print(result) # 结果为:1,即:受影响的行数
return redirect('user:show') -
备注
1
<input type="checkbox" name="is_delete" {% if user.is_delete %}checked{% endif %}> 用户是否删除
这样设置后,如果复选框被选中(checked),其值为
on
,如果没有被选中,其值为None
,所以is_delete
的值可以如此获得:1
is_delete = True if request.POST.get('is_delete') else False
-
-
查找
-
filter()
- 返回一个新的
QuerySet
,其中包含与给定查找参数相匹配的对象。 - 查询参数(
**kwargs
)的格式应在下文 Field lookups 中描述。多个参数通过底层 SQL 语句中的AND
连接。 - 如果你需要执行更复杂的查询(例如,带有
OR
语句的查询),你可以使用Q 对象
(*args
)。 - 示例
1
User.objects.filter(pk=1)
- 返回一个新的
-
exclude()
- 返回一个新的
QuerySet
,其中包含与给定查找参数不匹配的对象。 - 查询参数(
**kwargs
)的格式应在下文 Field lookups 中描述。多个参数通过底层 SQL 语句中的AND
连接,整个过程用NOT()
括起来。
- 返回一个新的
-
order_by()
默认情况下,
QuerySet
返回的结果是按照模型Meta
中的ordering
选项给出的排序元组排序的。你可以通过使用order_by
方法在每个QuerySet
的基础上覆盖这一点。 -
values()
- 返回一个
QuerySet
,当用作可迭代对象时,返回字典,而不是模型实例。 - 其中每一个字典都代表一个对象,键与模型对象的属性名相对应。
- 返回一个
-
all()
- 返回当前
QuerySet
(或QuerySet
子类)的 副本。 这在以下情况下很有用:你可能想传入一个模型管理器或一个QuerySet
,并对结果做进一步过滤。在任何一个对象上调用all()
后,你肯定会有一个QuerySet
可以使用。 - 当一个
QuerySet
被 执行 时,它通常会缓存其结果。如果数据库中的数据可能在QuerySet
被评估后发生了变化,你可以通过调用all()
对以前执行过的QuerySet
进行更新。 - 示例
1
User.objects.all()
- 返回当前
-
get()
返回与给定的查找参数相匹配的对象,其格式应该在 Field lookups 中描述。你应该使用保证唯一的查询,比如主键或唯一约束中的字段。
1
User.objects.get(pk=1)
-
count()
返回一个整数,表示数据库中与
QuerySet
匹配的对象数量。 -
first()
返回查询集匹配的第一个对象,如果没有匹配的对象,则返回
None
。如果QuerySet
没有定义排序,那么查询集自动按主键排序。这可能会影响聚合结果,如 Interaction with order_by() 中所述。 -
last()
与
first()
工作原理相同,但返回的是查询集中的最后一个对象。 -
exists()
-
模型关系(ORM)
-
1-1 (
OneToOneField
)-
1-1 概述
- 一对一的关系。概念上,这类似于
ForeignKey
与unique=True
,但关系的“反向”将直接返回一个单一对象。 - 最有用的是作为某种方式“扩展”另一个模型的主键;多表继承 是通过添加一个从子模型到父模型的隐式一对一关系来实现的,例如:
- 需要一个位置参数:模型将与之相关的类。这与
ForeignKey
的工作原理完全相同,包括关于 递归 和 惰性 关系的所有选项。 - 如果没有为
OneToOneField
指定related_name
参数,Django 将使用当前模型的小写名作为默认值。
- 一对一的关系。概念上,这类似于
-
创建模型
-
用户表
1
2
3
4
5
6
7
8
9
10
11
12
13## 用户表(主表)
class User(models.Model):
username = models.CharField(max_length=12, unique=True)
password = models.CharField(max_length=100)
phone = models.CharField(max_length=11, null=True, blank=True)
email = models.CharField(max_length=50, null=True, blank=True)
add_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.username
class Meta:
db_table = 'user' -
补充表
1
2
3
4
5
6
7
8
9
10
11## 补充表(从表)
class UserProfile(models.Model):
realname = models.CharField(max_length=12, null=True, blank=True)
address = models.CharField(max_length=200, null=True, blank=True)
age = models.IntegerField(null=True, blank=True)
job = models.CharField(max_length=50, null=True, blank=True)
gender = models.CharField(max_length=5, choices=(('boy', '男'), ('girl', '女')))
user = models.OneToOneField(to=User, on_delete=models.CASCADE) # 建立表之间的关系
class Meta:
db_table = 'userprofile'
-
-
添加数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19def register(request):
# 用户表
user = User()
user.username = '张三'
user.password = '123456'
user.phone = '13611111111'
user.email = '13611111111@163.com'
user.save()
# 补充表
userProfile = UserProfile()
userProfile.address = '北京'
userProfile.age = '22'
userProfile.gender = 'boy'
userProfile.realname = '张三丰'
userProfile.job = '唱、跳、rap'
userProfile.user_id = user.id
userProfile.save()
return HttpResponse('用户添加成功!') -
获取数据
级联查询:
对象.关系模型名(小写).属性名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<table class="table table-hover">
<tr>
<td>序号</td>
<td>用户名</td>
<td>真实姓名</td>
<td>电话</td>
<td>邮箱</td>
<td>住址</td>
</tr>
{% for user in users %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ user.username }}</td>
<td>{{ user.userprofile.realname }}</td>
<td>{{ user.phone }}</td>
<td>{{ user.email }}</td>
<td>{{ user.userprofile.address }}</td>
</tr>
{% endfor %}
</table>
-
-
1-n (
ForeignKey
)-
1-n 概述
- 一个一对多的关系。需要两个位置参数:模型相关的类和
on_delete
选项。 - 要创建一个与自己有一对多关系的对象,使用
odels.ForeignKey('self', on_delete=models.CASCADE)
。
- 一个一对多的关系。需要两个位置参数:模型相关的类和
-
1-n 参数
-
on_delete
-
当一个由
ForeignKey
引用的对象被删除时,Django 将模拟on_delete
参数所指定的 SQL 约束的行为。 -
CASCADE
- 级联删除。Django 模拟了 SQL 约束 ON DELETE CASCADE 的行为,也删除了包含 ForeignKey 的对象。
Model.delete()
在相关的模型上没有被调用,但是pre_delete
和post_delete
信号是为所有被删除的对象发送的。
-
PROTECT
通过引发
ProtectedError
,即django.db.IntegrityError
的子类,防止删除被引用对象。 -
RESTRICT
通过引发
RestrictedError
(django.db.IntegrityError
的一个子类)来防止删除被引用的对象。与PROTECT
不同的是,如果被引用的对象也引用了一个在同一操作中被删除的不同对象,但通过CASCADE
关系,则允许删除被引用的对象。 -
SET_NULL
设置
ForeignKey
为空;只有当null
为True
时,才有可能。 -
SET_DEFAULT
将
ForeignKey
设置为默认值,必须为ForeignKey
设置一个默认值。 -
SET()
将
ForeignKey
设置为传递给SET()
的值,或者如果传入可调用对象,则为调用它的结果。 在大多数情况下,传递可调用对象是必要的,以避免在导入models.py
时执行查询 -
DO_NOTHING
不采取任何行动。如果你的数据库后端强制执行引用完整性,这将导致一个
IntegrityError
除非你手动添加一个 SQLON DELETE
约束条件到数据库字段。
-
-
limit_choices_to
- 当使用
ModelForm
或管理中渲染该字段时,设置该字段的可用选择限制(默认情况下,查询集中的所有对象都可以选择)。可以使用字典、Q
对象,或者返回字典或Q
对象的可调用对象。 - 导致
ModelForm
上的对应字段只列出有is_staff=True
的Users
。这在 Django 管理中可能会有帮助。 - 如果
limit_choices_to
是或返回一个Q对象
,这对 复杂的查询 很有用,那么只有当该字段没有在ModelAdmin
中的raw_id_fields
中列出时,它才会对管理中可用的选择产生影响。
- 当使用
-
related_name
- 用于从相关对象到这个对象的关系的名称。这也是
related_query_name
的默认值(用于从目标模型反向过滤名称的名称)。请参阅 关联对象文档 以获得完整的解释和示例。请注意,当你在 抽象模型 是可用的。 - 如果你不希望 Django 创建一个反向关系,可以将
related_name
设置为'+'
或者以'+'
结束。
- 用于从相关对象到这个对象的关系的名称。这也是
-
related_query_name
- 目标模型中反向过滤器的名称。如果设置了,它默认为
related_name
或default_related_name
的值,否则默认为模型的名称。 - 和
related_name
一样,related_query_name
通过 一些特殊的语法 支持应用标签和类的插值。
- 目标模型中反向过滤器的名称。如果设置了,它默认为
-
to_field
关联对象的字段。默认情况下,Django 使用相关对象的主键。如果你引用了一个不同的字段,这个字段必须有
unique=True
。 -
db_constraint
- 控制是否应该在数据库中为这个外键创建一个约束。默认值是
True
,这几乎是你想要的;将其设置为False
对数据完整性非常不利。话虽如此,下面是一些你可能想要这样做的情况:- 你有无效的冗余数据
- 你正在共享你的数据库
- 如果将此设置为
False
,访问一个不存在的相关对象将引发DoesNotExist
异常。
- 控制是否应该在数据库中为这个外键创建一个约束。默认值是
-
swappable
- 控制迁移框架的反应,如果这个
ForeignKey
指向一个可交换的模型。如果它是True
——默认值-——那么如果ForeignKey
指向的模型与settings.AUTH_USER_MODEL
的当前值相匹配(或其他可互换模型配置),则关系将在迁移中使用对配置的引用而不是直接对模型进行存储。 - 只有当你确定你的模型应该总是指向换入的模型时,你才想把它覆盖为
False
,例如,如果它是一个专门为你的自定义用户模型设计的配置文件模型。 - 将它设置为
False
并不意味着你可以引用一个可交换的模型,即使它被交换了——False
意味着用这个外键进行的迁移将始终引用你指定的确切模型(所以如果用户试图用你不支持的 User 模型运行,它将失败,例如)。 - 如果不确定,就保留它在默认为
True
的状态。
- 控制迁移框架的反应,如果这个
-
-
创建模型
-
类别表
1
2
3
4
5
6
7
8
9
10# 主表
class Type(models.Model):
tname = models.CharField(max_length=200)
add_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.tname
class Meta:
db_table = 'type' -
商品表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# 从表
class Goods(models.Model):
gname = models.CharField(max_length=200)
price = models.DecimalField(max_digits=6, decimal_places=2)
sale_num = models.IntegerField(default=0)
store_num = models.IntegerField(default=20)
save_num = models.IntegerField(default=0)
# 1 - n
type = models.ForeignKey(to=Type, on_delete=models.CASCADE)
def __str__(self):
return self.gname
class Meta:
db_table = 'goods'
-
-
添加数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16def add_goods(request):
if request.method == 'POST':
gname = request.POST.get('gname')
price = request.POST.get('price')
type = request.POST.get('type')
try:
# 添加数据:create()
goods = Goods.objects.create(gname=gname, price=price, type_id=type)
if goods:
return HttpResponse('商品添加成功!')
except Exception as e:
return HttpResponse(e)
else:
types = Type.objects.all()
return render(request, 'goods/add.html', {'types': types})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{% extends 'base.html' %}
{% block title %}
商品添加
{% endblock %}
{% block content %}
<h3 style="text-align: center">商品添加</h3>
<form class="form-horizontal" action="{% url 'goods:add' %}" method="post">
{% csrf_token %}
<div class="form-group">
<label class="col-sm-2 control-label">商品名称</label>
<div class="col-sm-6">
<input type="text" class="form-control" placeholder="商品名称" name="gname">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">商品价格</label>
<div class="col-sm-6">
<input type="text" class="form-control" placeholder="商品价格" name="price">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">商品分类</label>
<div class="col-sm-6">
<select class="form-control" name="type">
{% for type in types %}
<option value="{{ type.id }}">{{ type.tname }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">添加商品</button>
</div>
</div>
</form>
{% endblock %} -
获取数据
-
一获取多(主获取从)
根据类别获取商品
1
2
3def detail_type(request, tid):
type = Type.objects.get(pk=tid)
return render(request, 'goods/detail_type.html', {'type': type})1
2
3
4
5
6
7
8
9
10
11
12
13<table class="table table-hover">
{% for goods in type.goods_set.all %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ goods.gname }}</td>
<td>{{ goods.price }}</td>
</tr>
{% empty %}
<tr>
<td colspan="3">还没有商品,赶快添加吧!</td>
</tr>
{% endfor %}
</table>隐性属性
对象.关联对象(小写)_set.all、filter
等 -
多获取一(从获取主)
根据商品获取类别
1
2
3def detail_goods(request, gid):
goods = Goods.objects.get(pk=gid)
return render(request, 'goods/detail_goods.html', {'goods': goods})1
2
3
4
5
6
7
8
9
10
11
12
13
14{% extends 'base.html' %}
{% block title %}
商品详情
{% endblock %}
{% block content %}
<p>商品名:{{ goods.gname }}</p>
<p>商品价格:{{ goods.price }}</p>
<p>商品销量:{{ goods.sale_num }}</p>
<p>商品库存:{{ goods.store_num }}</p>
<p>商品收藏量:{{ goods.save_num }}</p>
<p>所属分类:{{ goods.type.tname }}</p>
{% endblock %}
-
-
删除数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17{% extends 'base.html' %}
{% block title %}
所有分类
{% endblock %}
{% block content %}
<h2 style="text-align: center">商城所有分类</h2>
<ul class="list-group" style="text-align: center">
{% for type in types %}
<li class="list-group-item">
<a href="{% url 'goods:detail_type' type.id %}">{{ type.tname }}</a> |
<a href="{% url 'goods:delete_type' %}?id={{ type.id }}">删除分类</a>
</li>
{% endfor %}
</ul>
{% endblock %}1
2
3
4
5
6# 删除分类:delete()
def delete_type(request):
id = request.GET.get('id')
type = Type.objects.get(pk=id)
type.delete()
return redirect('goods:show_type')- 如果设置
on_delete=models.CASCADE
,删除时会级联删除。例如:删除某个商品分类,会将属于这个分类的所有商品一并删除。 - 如果设置
on_delete=models.PROTECT
,删除时会报错。例如:删除某个商品分类,如果商品表中有属于这个分类的商品,则会报错。 - 如果设置
on_delete=models.DO_NOTHING
,删除时不会采取任何行动。例如:删除某个商品分类,但是属于这个分类的所有商品并不会被删除,仅仅是删除了分类表中的数据,商品表中的数据无论是否关联都不会被删除。
- 如果设置
-
-
m-n (
ManyToManyField
)-
m-n 概述
- 一个多对多的关系。需要一个位置参数:模型相关的类,它的工作原理与
ForeignKey
完全相同,包括 递归 和 惰性 关系。 - 可以通过字段的
RelatedManager
来添加、删除或创建相关对象。 - 在幕后,Django 创建了一个中间连接表来表示多对多的关系。默认情况下,这个表名是使用多对多字段的名称和包含它的模型的表名生成的。由于有些数据库不支持超过一定长度的表名,这些表名将被自动截断,并使用唯一性哈希,例如
author_books_9cdf
。你可以使用db_table
选项手动提供连接表的名称。
- 一个多对多的关系。需要一个位置参数:模型相关的类,它的工作原理与
-
m-n 参数
-
参数概述
ManyToManyField
接受一组额外的参数 —— 都是可选的 —— 控制关系如何运作。ManyToManyField
不支持validators
。null
没有效果,因为没有办法在数据库层面要求建立关系。
-
related_name
与
ForeignKey.related_name
相同。 -
related_query_name
-
limit_choices_to
-
symmetrical
-
仅在自身上定义多对多字段关系时。考虑以下模型
1
2
3
4from django.db import models
class Person(models.Model):
friends = models.ManyToManyField("self") -
当 Django 处理这个模型时,它识别出它本身有一个
ManyToManyField
,因此,它没有给Person
类添加person_set
属性。相反,ManyToManyField
被认为是对称的,也就是说,如果我是你的朋友,那么你就是我的朋友。 -
如果你不想让
self
的多对多关系对称,可以将symmetrical
设置为False
。这样会强制 Django 添加反向关系的描述符,允许ManyToManyField
关系是非对称的。
-
-
through
-
Django 会自动生成一个表来管理多对多关系。但是,如果你想手动指定中间表,你可以使用
through
选项来指定代表你要使用的中间表的 Django 模型。 -
这个选项最常见的用法是当你想把 额外的数据与多对多关系 联系起来。
-
如果你没有指定一个显式的
through
模型,你仍然可以使用一个隐式的through
模型类来直接访问为保持关联而创建的表。它有三个字段来链接模型。- 如果源模型和目标模型不同,则会生成以下字段:
id
:关系的主键。<containing_model>_id
:声明ManyToManyField
的模型的id
。<other_model>_id
:ManyToManyField
指向的模型的id
。
- 如果
ManyToManyField
指向的来源和目标是相同的模型,下面的字段会生成:id
:关系的主键。from_<model>_id
:指向模型的实例(即源实例)的id
。to_<model>_id
:关系所指向的实例(即目标模型实例)的id
。
- 如果源模型和目标模型不同,则会生成以下字段:
-
这个类可以像普通模型一样,用于查询给定模型实例的关联记录:
1
Model.m2mfield.through.objects.all()
-
-
through_fields
-
只有当指定了一个自定义的中间模型时才会使用,Django 通常会决定使用中介模型的哪些字段来自动建立多对多的关系。然而,考虑以下模型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22from django.db import models
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=('group', 'person'),
)
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
inviter = models.ForeignKey(
Person,
on_delete=models.CASCADE,
related_name="membership_invites",
)
invite_reason = models.CharField(max_length=64) -
Membership
对Person
有 两个 外键(person
和inviter
),这就使得两者的关系变得模糊不清,Django 无法知道应该使用哪个外键。在这种情况下,你必须使用through_fields
明确指定 Django 应该使用哪个外键,就像上面的例子一样。 -
through_fields
接受一个二元元组('field1', 'field2')
,其中field1
是定义在ManyToManyField
上的模型(本例中为group
)的外键名称,field2
是目标模型(本例中为person
)的外键名称。 -
当你在中间模型上有一个以上的外键到任何一个(甚至两个)参与多对多关系的模型时,你 必须 指定
through_fields
。这也适用于 递归关系,当使用一个中间模型,并且该模型有两个以上的外键,或者你想明确指定 Django 应该使用哪两个外键。
-
-
db_table
要创建的用于存储多对多数据的表的名称。如果没有提供这个表名,Django 将根据以下表名创建一个默认表名:定义关系的模型表和字段本身的名称。
-
db_constraint
-
控制是否应该在数据库中为中间表的外键创建约束。默认值是
True
,这几乎是你想要的;将其设置为False
对数据完整性非常不利。话说回来,下面是一些你可能想要这样做的情况:- 你有无效的冗余数据
- 你正在共享你的数据库
-
同时传递
db_constraint
和through
会引发错误。
-
-
swappable
- 控制迁移框架的反应,如果这个
ManyToManyField
指向一个可交换的模型。如果它是True
——默认值——那么如果ManyToManyField
指向的模型与settings.AUTH_USER_MODEL
的当前值相匹配(或其他可交换模型配置),关系将被存储在迁移中,使用对配置的引用,而不是直接对模型的引用。 - 只有当你确定你的模型应该总是指向换入的模型时,你才想把它覆盖为
False
,例如,如果它是一个专门为你的自定义用户模型设计的配置文件模型。 - 如果不确定,就保留它在默认为
True
的状态。
- 控制迁移框架的反应,如果这个
-
-
创建模型
-
用户表
1
2
3
4
5
6
7
8
9
10
11
12
13# 主表
class User(models.Model):
username = models.CharField(max_length=12, unique=True)
password = models.CharField(max_length=100)
phone = models.CharField(max_length=11, null=True, blank=True)
email = models.CharField(max_length=50, null=True, blank=True)
add_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.username
class Meta:
db_table = 'user' -
商品表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 从表
class Goods(models.Model):
gname = models.CharField(max_length=200)
price = models.DecimalField(max_digits=6, decimal_places=2)
sale_num = models.IntegerField(default=0)
store_num = models.IntegerField(default=20)
save_num = models.IntegerField(default=0)
# 1 - n
type = models.ForeignKey(to=Type, on_delete=models.CASCADE)
# m - n
users = models.ManyToManyField(to=User)
def __str__(self):
return self.gname
class Meta:
db_table = 'goods'
-
-
添加数据
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{% extends 'base.html' %}
{% block title %}
商品详情
{% endblock %}
{% block content %}
<p>商品名:{{ goods.gname }}</p>
<p>商品价格:{{ goods.price }}</p>
<p>商品销量:{{ goods.sale_num }}</p>
<p>商品库存:{{ goods.store_num }}</p>
<p>商品收藏量:<span>{{ goods.save_num }}</span>
<span id="save_icon" class="glyphicon glyphicon-star" style="color: black"></span>
</p>
<p>所属分类:{{ goods.type.tname }}</p>
<button id="btn_add" type="button" class="btn btn-success">加入购物车</button>
{% endblock %}
{% block myjs %}
<script>
$('#btn_add').click(function () {
$.getJSON("{% url 'goods:add_cart' %}", {'id': {{ goods.id }}}, function (data) {
if (data.msg === 'success') {
alert('添加购物车成功')
}
})
})
</script>
{% endblock %}1
2
3
4
5
6
7
8
9
10# 商品添加到购物车
def add_cart(request):
id = request.GET.get('id')
# 找到goods对象
goods = Goods.objects.get(pk=id)
user = User.objects.get(pk=1)
# 添加数据:对象A.集合.add(对象B)
# goods.users.add(user) 或 user.goods_set.add(goods)
user.goods_set.add(goods)
return JsonResponse({'msg': 'success'}) -
获取数据
1
2
3
4# 显示购物车商品
def show_cart(request):
user = User.objects.get(pk=1)
return render(request, 'goods/show_cart.html', {'user': user})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{% extends 'base.html' %}
{% block title %}
购物车
{% endblock %}
{% block content %}
<h1 style="text-align: center">{{ user.username }} 购物车中的商品如下</h1>
<table class="table table-hover">
<tr>
<td>序号</td>
<td>商品名称</td>
<td>价格</td>
<td>数量</td>
<td>操作</td>
</tr>
{# 获取数据 #}
{% for goods in user.goods_set.all %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ goods.gname }}</td>
<td>{{ goods.price }}</td>
<td>1</td>
{# 删除数据 #}
<td><a href="{% url 'goods:delete_cart' %}?id={{ goods.id }}">删除</a></td>
</tr>
{% endfor %}
</table>
{% endblock %} -
删除数据
1
2
3
4
5
6
7
8# 删除购物车商品
def delete_cart(request):
id = request.GET.get('id')
goods = Goods.objects.get(pk=id)
user = User.objects.get(pk=1)
# 删除数据:remove()
user.goods_set.remove(goods)
return redirect('goods:show_cart')
-