Django定义数据模型
1. 简单说明
定义模型就是实现一个django.db.models.Model
类的子类,每个模型被表示为django.db.models.Model
类的子类。每个模型有许多类变量,它们都表示模型里的一个数据库字段,每个字段都是Field
类的实例,模型的文件名称默认为models.py
,也可使用其他名称,一般放在应用内。
模型由三个部分组成:
Filed
类型字段- 魔术方法
__str__
Model
元数据声明
代码示例:
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
2. 定义字段
字段的定义包括字段名、字段类型和字段选项,示例代码如下:
question = models.CharField(max_length=200,blank=True)
其中,question
为字段名,CharField
为字段类型,max_length
和blank
为字段选项。
Django在 django.db.models.fields
模块中定义了可用的字段类型。为了方便,Django
已将fields
模块导入到django.db.models
模块中。通常,先用from django.db import models
导入models
模块,然后用models.xxxField
来引用字段类型。
2.1. 字段类型
2.1.1. 数值字段
AutoField
:自动增量,32位整数,取值范围是1~2^31-1
BigAutoField
:自动增量,64 位整数,取值范围是1~2^63−1
,也就是1-9223372036854775807
SmallAutoField()
自动增量,存储小整数。取值范围是−2^15~2^15−1
,也就是1-32767
IntegerField
:存储整数,取值范围是−2^31~2^31−1
,字段的默认表单控件为NumberInput
BigIntegerField
:64位整数,取值范围是−263~263−1
,字段的默认表单控件为TextInput
SmallIntegerField
:存储小整数。取值范围是−2^15~2^15−1
PositiveIntegerField
:存储非负整数。取值范围是0~2^31−1
PositiveSmallIntegerField
:储非负小整数。取值范围是0~2^15−1
PositiveBigIntegerField()
整型,范围0-9223372036854775807
BooleanField
:存储True
或False
,字段的默认表单控件为CheckboxInput
FloatField
:存储浮点数字。字段的默认表单控件为NumberInput
DecimalField(max_digits=None, decimal_places=None)
存储固定精度的十进制数字段,字段值为Decimal
实例,字段的默认表单控件为NumberInput
,max_digits
:小数总长度,decimal_places
:小数位长度
2.1.2. 文本字段
CharField
:存储字符串。字段的默认表单控件为TextInput
,必须添加max_length=
选项TextField
:存储大量文本。字段的默认表单控件为Textarea
EmailField
:存储 E-mail 地址URLField
:存储URL,字段的默认表单控件为TextInput
GenericIPAddressField
:储字符串格式的IPv4
或IPv6
地址,字段的默认表单控件为TextInput
SlugField
:字符串字段,存储Slug
数据,只包含字母、数字、下划线或连字符
2.1.3. 时间字段
DateField
:存储日期,字段值为datetime.date
实例。字段的默认表单控件为TextInput
TimeField
:存储时间,字段值为datetime.time实例。字段的默认表单控件为TextInput
DateTimeField
:存储日期时间,字段值为datetime.datetime
实例,字段的默认表单控件为TextInput
DurationField
:存储时间段
2.1.4. 文件相关字段
FileField
:存储文件。字段的默认表单控件为ClearableFileInput
ImageField
:存储图片,字段的默认表单控件为ClearableFileInput
FilePathField(path='', match=None, recursive=False, allow_files=True, allow_folders=False, max_length=100)
:存储文件路径,path
:文件夹路径,match=None
:正则匹配,recursive=False
:递归子文件夹,allow_files=True
:允许文件,allow_folders=False
:允许文件夹
2.1.5. 其他类型字段
BinaryField
:存储原始二进制数据UUIDField
:存储唯一标识符,字段值为UUID类实例JSONField(encoder=None, decoder=None)
:存储 JSON 编码数据,encoder
,用于序列化标准 JSON 序列化器不支持的数据字段(例如datetime.datetime
或UUID
),decoder
,用于反序列化从数据库中获取的值
2.2. 字段选项
null
:默认为False
。为True
时,Django在字段无数据时将空值NULL
存入数据库(字符串字段存入空字符串)blank
:默认为False
。为True
时,字段允许为空,即表单验证将允许输入空值。blank
影响数据验证,null
影响数据库数据存储choices
:为字段定义选择项。字段值为选择项中的列表或元组中的值db_column
:定义字段在数据库表中的列名称。未设置时,Django用模型中的字段名作为数据库表的列名称db_index
:为True
时,为该字段创建数据库索引db_tablespace
:若为字段创建了索引,则为字段索引设置数据库的表空间名称default
:设置字段默认值editable
:默认是True
。为False
时,字段不在模型表单中显示error_messages
:设置错误提示信息。该设置会覆盖默认的错误提示信息help_text
:设置字段的帮助信息primary_key
:设置为True
时,字段成为模型的主键unique
:设置为True
时,字段值在整个表中必须是唯一的unique_for_date
:设置为日期或日期时间字段名,关联的两个字段值在整个表中必须是唯一的unique_for_month
:类似unique_for_date
。与关联的月份唯一unique_for_year
:类似unique_for_date
。与关联的年份唯一verbose_name
:为字段设置备注名称validators
:为字段设置校验器related_name
:为字段设置对象内调用的别名
2.2.1. 特殊的字段选项choices
choices
是一个映射或可迭代对象,用作模型字段的选项。
提供choices
后,字段的默认表单小部件会变为选择框,并且选项会被模型验证强制执行。
choices
在 Django 中有以下几种形式:
- 简单的列表或元组形式
这是最常见的形式,choices
定义为一个包含 (值, 可读名称)
的列表或元组。适用于定义一组固定的选项。
# 1、列表形式 (定义在模型类之外)
YEAR_IN_SCHOOL_CHOICES = [
("FR", "Freshman"),
("SO", "Sophomore"),
("JR", "Junior"),
("SR", "Senior"),
("GR", "Graduate"),
]
# 2、元组形式 (定义在模型类之外)
# YEAR_IN_SCHOOL_CHOICES = (
# ("FR", "Freshman"),
# ("SO", "Sophomore"),
# ("JR", "Junior"),
# ("SR", "Senior"),
# ("GR", "Graduate"),
#)
class Student(models.Model):
# 3、列表/元组形式(定义在模型类内部,即将选项列表定义为类常量)
# YEAR_IN_SCHOOL_CHOICES = [
# ("FR", "Freshman"),
# ("SO", "Sophomore"),
# ("JR", "Junior"),
# ("SR", "Senior"),
# ("GR", "Graduate"),
# ]
year_in_school = models.CharField(
max_length=2,
choices=YEAR_IN_SCHOOL_CHOICES,
default="FR",
)
- 字典映射形式
在 Django 5.0 中,choices
支持使用字典定义,每个键表示实际存储的值,值为可读名称。适合需要清晰键值对的场景。
# 1、定义在模型类之外
YEAR_IN_SCHOOL_CHOICES = {
"FR": "Freshman",
"SO": "Sophomore",
"JR": "Junior",
"SR": "Senior",
"GR": "Graduate",
}
class Student(models.Model):
# 2、定义在模型类内部
# YEAR_IN_SCHOOL_CHOICES = {
# "FR": "Freshman",
# "SO": "Sophomore",
# "JR": "Junior",
# "SR": "Senior",
# "GR": "Graduate",
# }
year_in_school = models.CharField(
max_length=2,
choices=YEAR_IN_SCHOOL_CHOICES,
default="FR",
)
- 分组形式
通过嵌套元组或字典分组的方式,将选项分为不同类别,适用于多级选择的场景。
# 1、使用嵌套元组 (定义在模型类之外)
MEDIA_CHOICES = [
("Audio", [("vinyl", "Vinyl"), ("cd", "CD")]),
("Video", [("vhs", "VHS Tape"), ("dvd", "DVD")]),
("unknown", "Unknown"),
]
# 2、使用字典 (定义在模型类之外)
# MEDIA_CHOICES = {
# "Audio": {"vinyl": "Vinyl", "cd": "CD"},
# "Video": {"vhs": "VHS Tape", "dvd": "DVD"},
# "unknown": "Unknown",
#}
class Media(models.Model):
# 3、使用嵌套元组 (定义在模型类内部)
# MEDIA_CHOICES = [
# ("Audio", [("vinyl", "Vinyl"), ("cd", "CD")]),
# ("Video", [("vhs", "VHS Tape"), ("dvd", "DVD")]),
# ("unknown", "Unknown"),
# ]
media_type = models.CharField(
max_length=10,
choices=MEDIA_CHOICES,
default="unknown",
)
- 可调用对象
将 choices
定义为一个无参可调用对象(如函数),函数返回一个合法的 choices
结构(例如列表或字典)。适合动态生成选项的场景。
def get_currencies():
return {i: i for i in ["USD", "EUR", "JPY"]}
class Expense(models.Model):
currency = models.CharField(max_length=3, choices=get_currencies)
- 枚举类型
使用 Django 的 TextChoices
、IntegerChoices
等枚举类来定义 choices
,适合需要常量化、易读性高的选择项。Django 自动生成 .choices
、.labels
、.values
等属性,便于管理。
class Student(models.Model):
class YearInSchool(models.TextChoices):
FRESHMAN = "FR", "Freshman"
SOPHOMORE = "SO", "Sophomore"
JUNIOR = "JR", "Junior"
SENIOR = "SR", "Senior"
GRADUATE = "GR", "Graduate"
year_in_school = models.CharField(max_length=2, choices=YearInSchool.choices)
- 自定义数据类型的枚举
在 Choices
类的基础上扩展,可以将 choices
与特定数据类型(如 date
或 datetime
)结合,适合需要特定类型支持的场景,如日期 date
import datetime
class MoonLandings(datetime.date, models.Choices):
APOLLO_11 = 1969, 7, 20, "Apollo 11 (Eagle)"
APOLLO_12 = 1969, 11, 19, "Apollo 12 (Intrepid)"
补充:
在 Choices
类的基础上扩展,可以将 choices
与特定数据类型(如 date
或 datetime
)结合,适合需要特定类型支持的场景,如日期 date
import datetime
class MoonLandings(datetime.date, models.Choices):
APOLLO_11 = 1969, 7, 20, "Apollo 11 (Eagle)"
APOLLO_12 = 1969, 11, 19, "Apollo 12 (Intrepid)"
类似的方式还可以应用于处理其他特定数据类型,例如 datetime
、time
、Decimal
、UUID
等。以下是一些常见数据类型的示例:
- 处理
datetime
类型的choices
假设我们需要记录一些重要的时间点,可以使用 datetime.datetime
和 models.Choices
来定义这些选择:
import datetime
from django.db import models
class ImportantMoments(datetime.datetime, models.Choices):
NEW_YEAR = 2024, 1, 1, 0, 0, 0, "New Year 2024"
SUMMER_SOLSTICE = 2024, 6, 21, 12, 0, 0, "Summer Solstice 2024"
WINTER_SOLSTICE = 2024, 12, 21, 12, 0, 0, "Winter Solstice 2024"
class Event(models.Model):
moment = models.DateTimeField(choices=ImportantMoments.choices)
这里的 moment
字段的可选项是特定的 datetime
值,比如“2024 年的新年”等。这样我们可以确保用户只能选择预定义的时间点。
- 处理
time
类型的choices
如果需要记录某些特定的时间(比如营业时间、特殊的时刻),可以用 datetime.time
和 models.Choices
:
import datetime
from django.db import models
class BusinessHours(datetime.time, models.Choices):
OPENING = 9, 0, 0, "Opening Time"
CLOSING = 18, 0, 0, "Closing Time"
LUNCH_BREAK = 12, 0, 0, "Lunch Break"
class Store(models.Model):
operation_time = models.TimeField(choices=BusinessHours.choices)
在这个例子中,operation_time
字段的可选值只能是 BusinessHours
定义的几个特定时刻,比如开门时间、关门时间和午休时间。
- 处理
Decimal
类型的choices
有时我们需要记录固定的金额,可以用 Decimal
和 models.Choices
。例如,对于定价的选择,可以使用 Decimal
来精确表示金额。
from decimal import Decimal
from django.db import models
class FixedPrices(Decimal, models.Choices):
BUDGET = Decimal("9.99"), "Budget Option"
STANDARD = Decimal("19.99"), "Standard Option"
PREMIUM = Decimal("29.99"), "Premium Option"
class Product(models.Model):
price = models.DecimalField(max_digits=5, decimal_places=2, choices=FixedPrices.choices)
在这里,price
字段的选项是特定的价格,比如“预算选项”、“标准选项”等,可以确保用户只能选择固定的金额。
- 处理
UUID
类型的choices
如果使用 UUID
来标识某些特定的设备或版本,我们可以用 uuid.UUID
和 models.Choices
。例如,在记录设备时,指定唯一的 UUID
作为选项:
import uuid
from django.db import models
class KnownDevices(uuid.UUID, models.Choices):
DEVICE_A = uuid.UUID("123e4567-e89b-12d3-a456-426614174000"), "Device A"
DEVICE_B = uuid.UUID("123e4567-e89b-12d3-a456-426614174001"), "Device B"
DEVICE_C = uuid.UUID("123e4567-e89b-12d3-a456-426614174002"), "Device C"
class Device(models.Model):
device_id = models.UUIDField(choices=KnownDevices.choices)
这样可以确保 device_id
字段只能是已知设备的 UUID
。
- 处理
float
类型的choices
在某些情况下,如果我们需要用浮点数记录物理常量(比如π值的不同精度),可以使用 float
类型。
from django.db import models
class PiValues(float, models.Choices):
LOW_PRECISION = 3.14, "Low Precision Pi"
MEDIUM_PRECISION = 3.14159, "Medium Precision Pi"
HIGH_PRECISION = 3.1415926535, "High Precision Pi"
class Measurement(models.Model):
pi_value = models.FloatField(choices=PiValues.choices)
通过这种方法,pi_value
字段仅限于选择特定精度的 π 值。
2.2.1.1. 取数据
只要是choices
参数的字段,如果想要获取对应信息,固定写法get_字段名_display()
user_obj = models.Client.objects.filter(pk=1).first()
print(user_obj.get_gender_display())
# 输出:男
那么还有一个用户名为tom
没有书写固定关系,他返回的会是什么?
user_obj = models.Client.objects.filter(pk=4).first()
print(user_obj.get_gender_display())
# 输出:4
3. 字段详解
3.1. 文本字段
3.1.1. CharField
CharField
用于存储短字符串,其最大长度可以在模型定义中指定。例如,如果你有一个字段用于存储用户的电话号码或电子邮件地址,那么使用CharField
会更合适。默认情况下,CharField
使用最大长度为255
的字符串。
3.1.2. TextField
与CharField
不同,TextField
用于存储长文本数据。它没有最大长度限制,适用于存储如文章内容、评论或描述字段等大量文本数据。在模型定义中使用TextField
,你可以将数据存储为长文本形式。
在选择使用CharField
还是TextField
时,需要考虑数据的大小和用途。如果你的数据量较小,如电话号码、邮箱地址等,使用CharField
更为合适。而如果你的数据量较大,如文章内容、评论等,则应选择TextField
。
另外,值得注意的是,虽然TextField
没有最大长度限制,但在实际应用中应考虑数据库的性能和可扩展性。大量的大型文本字段可能会对数据库性能产生负面影响,因此需要谨慎使用。
3.2. 时间字段
3.2.1. DateField
class DateField(auto_now=False, auto_now_add=False, **options)
一个日期,在Python
中用一个datetime.date
实例表示。有一些额外的、可选的参数。
3.2.1.1. DateField.auto_now
每次保存对象时,自动将该字段设置为现在。对于“最后修改”的时间戳很有用。请注意,当前日期总是被使用,而不仅仅是一个你可以覆盖的默认值。
只有在调用Model.save()
时,该字段才会自动更新。当以其他方式对其他字段进行更新时,如QuerySet.update()
,该字段不会被更新,尽管你可以在这样的更新中为该字段指定一个自定义值。
3.2.1.2. 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
选项是相互排斥的。这些选项的任何组合都会导致错误。
备注:目前,将
auto_now
或auto_now_add
设置为True
,将导致该字段设置为editable=False
和blank=True
。
备注:
auto_now
和auto_now_add
选项将始终使用创建或更新时默认时区的日期。如果你需要一些不同的东西,你可能需要考虑使用你自己的可调用的默认值,或者覆盖save()
而不是使用auto_now
或auto_now_add
;或者使用DateTimeField
而不是DateField
,并决定如何在显示时间处理从日期时间到日期的转换。
3.2.2. DateTimeField
class DateTimeField(auto_now=False, auto_now_add=False, **options)
一个日期和时间,在Python
中用一个datetime.datetime
实例表示。与DateField
一样,使用相同的额外参数。
该字段的默认表单部件是一个单独的DateTimeInput
。管理中使用两个单独的TextInput
部件,并使用JavaScript
快捷方式。
3.3. 文件字段
文件字段赋值是必须为
flie
对象或者其子类,其实实现.name
与._committed
数据的对象应该也可以直接赋值
MyFile1.objects.create(name="test",file=fliedir)
# fliedir必须为`flie`对象
使用save()
方法时候,可以分为分开参数为name
与committed
from django.core.files.base import ContentFile
file_content = b"Hello, World!"
file = ContentFile(file_content)
obj = MyModel()
obj.my_file.save('test.txt', file)
obj.save()
4. Meta选项
4.1. 初识Meta内部类
每个模型类(Model)下都有一个子类Meta
,这个子类就是定义元数据的地方。Meta
类封装了一些数据库的信息,称之为Model
的元数据。Django会将Meta
中的元数据选项定义附加到Model
中。常见的元数据定义有 db_table
(数据表名称)、abstract
(抽象类)、ordering
(字段排序)等,Meta
作为内部类,它定义的元数据可以让admin
管理后台对人类更加友好,数据的可读性更高。
Meta
定义的元数据相当于Model
的配置信息,所以我们可以根据自己的需求进行选择性的添加。当没有需要的时候也可以不定义Meta
,这个时候Django会应用默认的Meta
元数据。
4.2. Meta类元数据
通过上面的介绍我们知道Meta
类的作用就是用于定义Model
的元数据,即不属于Model
的字段,但是可以用来标识字段一些属性,下面我们介绍Meta
定义的常见元数据以及如何在Model
中使用它们。
4.2.1. abstract[抽象类标识]
一个布尔类型的变量。这个属性是定义当前的模型是不是一个抽象类。所谓抽象类是不会对应数据库表的(当作基类使用)。一般我们用它来归纳一些公共属性字段,然后继承它的子类可以继承这些字段。如果abstract = True
这个model
就是一个抽象类。
4.2.2. ordering[排序]
用于执行获取对象列表时的排序规则。它是一个字符串的列表或元组对象,它的使用格式是由代表字段的字符串和一个表明降序的'-'
构成。当字段名前面没有'-'
时,将默认使用升序排列。使用'?'
将会随机排列。示例如下所示:
ordering=["add_time"] #按照升序排序
ordering=["-add_time"]#按照降序
ordering=["?add_time"]#随机排序
#同时指定多个字段来进行排序
ordering=['add_time','-last_login_time']#先按升序,在按降序
4.2.3. get_latest_by[最新排序]
指定一个DateField
或者DateTimeField
字段的名字,即model
的属性名字。使用示例如下:
get_latest_by = "order_date"
这个设置让你在使用模型管理器的lastest()
方法时,默认使用order_date
指定字段来排序。
4.2.4. order_with_respect_to[关联排序]
这个选项一般用于多对多的关系中,它指向一个关联对象并将该对象进行排序,使用元数据项后你会得到一个 get_xxx_order()
和set_xxx_order()
的方法,通过它们你可以设置或者得到排序的对象。
4.2.5. verbose_name[展示名称]
数据的友好展示名称
4.2.6. verbose_name_plural[复数展示名称]
这个元数据主要用在管理后台的展示上,verbose_name_plural
是模型类的复数名 。如果不设置的话,Django会使用小写的模型名作为默认值,并且在结尾加上 s
。通过此项元数据设置名字可以去掉s
。
4.2.7. db_table[数据表名称]
这个字段用于指定数据表的名称,通常没有特别需求,将按照 Django 默认的规则生成 Model 对应的数据库表名。
#定义该model在数据库中的表名称
db_table = 'Students'
#使用自定义的表名,可以通过以下属性
table_name = 'my_owner_table'
4.2.8. app_lable[应用命名空间]
这个选项只在一种使用情形,就是你的模型不在默认的应用程序包下的models.py
文件中,这时候需要指定你这个模型是哪个应用程序的app_label = 'app_name'
。
4.2.9. managed[管理数据]
它是一个布尔类型的变量,默认为Ture
,代表Django会管理数据的生命周期,即利用Django提供的syncdb
和reset
命令可以完成创建和删除数据表。如果为False
,则不会对此模型执行数据库表创建或删除操作。比如数据表之间存在ManyToMany
的关系,在指定为managed=False
的情况下,Django不会自动创建中间表,需要我们自己手动创建。
4.2.10. indexs[索引]
它是一个列表类型的元数据项,用来定义Model
的索引,列表中的每一个元素都是Index
类型的实例。
Index引自django.db.models.indexes.Index
4.2.11. default_permissions[默认权限]
Django默认会给每一个定义的Model
设置三个权限即添加、更改、删除,它使用格式:
default_permissions=('add','change','delete','view')
4.2.12. permissions[权限]
除了 Django 默认给Model
添加的三个权限之外,还可以通过permisssions
给Model
添加额外的权限。不过permissions
是一个包含二元组的元组或者列表,所以使用时应该注意格式,即permissions=[(权限代码,权限名称)]
,示例如下所示:
permissions = [(have_read_permission', '有读的权限')]
4.2.13. unique_together[共同唯一]
这个选项用于下面情形:当你需要通过两个字段保持唯一性时使用。比如用户的姓名(name
)和 身份证号码(ID number
)两者的组合必须是唯一的,那么需要这样设置:
unique_together = (("first_name", "last_name"),)
一个ManyToManyField
不能包含在unique_together
中。如果你需要验证ManyToManyField
字段的唯一验证,尝试使用through
属性进行关联。
4.2.14. proxy[代理]
默认值为为False
,如果设置成Ture
,则表示为基类、父类的代理模型。这个选项在后续章节还会进行相关介绍,它的主要作用就是创建父模型的代理模型。
4.2.15. db_tablespace[表空间]
表空间,用于优化数据库性能,常用于Oracle
、PostgerSQL
数据库。MySQL
数据库不支持表空间,所以当数据存储后端数据库不支持的时候,Django会在自动忽略这个元数据选项。