Django-第二部分(熟练掌握)-模型(Model)设计-定义模型类(基础)
目标
前面已经介绍了如何快速入门,现在开始熟练掌握一些知识
- 掌握定义模型类
回顾
a、模型(Model)设计流程过程说明
相关指导
a、官方文档介绍
一、字段选项
1.1、字段枚举介绍
参数名 | 参数作用 | 备注说明 |
---|---|---|
primary_key | 是否为主键 | a、若为 True, 则该字段会成为模型的主键字段;b、如果 Django 看到你显式的设了 Field.primary_key ,将不会自动在表(模型)中添加 id 列。注意:每个模型都需要拥有一个设置了 primary_key=True 的字段(无论是显式的设置还是 Django 自动设置) |
default | 默认值 | 可以是一个值或者可调用对象。注意:如果可调用 ,每有新对象被创建它都会被调用。 |
null | 是否可以为空 | a、True意味这个数据库里这个字段可以存储为null空值;b、默认值是 False;注意:Django对于空白的CharField和TextField永远不会存为null空值,而是存储空白字符串'',所以常与default=''搭配使用 |
blank | 是否必需 | a、如果为True,该字段允许不填。默认为False。注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。 |
choices | 选择列表 | 注意:由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。 |
max_length | 最大字符长度 | |
max_digits | 小数点位数总数 | |
decimal_places | 小数点后的数字位数 | |
auto_now | 最后一次修改自动设置当前时间 | a、 每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false; 注意:auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果 |
auto_now_add | 第一次创建自动设置当前时间 | a、当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false; 注意:auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果 |
on_delete | 联动删除设置 | |
unique | 唯一值 | 如果为 True, 这个字段在表中必须有唯一值 |
verbose_name | 备注名 | 注意:如果未指定该参数值,Django会自动使用该字段的属性名作为该参数值,并且把下划线转换为空格。 |
db_column | 字段的名称 | 注意:如果未指定,则使用属性的名称 |
db_index | 是否创建索引 | a、若值为 True, 则在表中会为此字段创建索引 |
related_name | 反向查询 | |
through | ||
db_tablespace | The name of the database tablespace to use for this field’s index, if this field is indexed. The default is the project’sDEFAULT_INDEX_TABLESPACE setting, if set, or the db_tablespace of the model, if any. If the backend doesn’t support tablespaces for indexes, this option is ignored. | |
editable | 如果False,该字段将不会显示在管理员或任何其他ModelForm…它们也会在模型验证…默认值是True. | |
error_messages | 这个error_messages参数使您可以重写字段将引发的默认消息。输入具有匹配要重写的错误消息的键的字典。 | |
help_text | 与表单小部件一起显示的额外“帮助”文本。即使您的字段没有在表单上使用,它对于文档也很有用。 | |
unique_for_date | 将此设置为DateField或DateTimeField若要求此字段对于日期字段的值是唯一的 | |
unique_for_month | ||
unique_for_year | ||
validators |
1.2、常用字段使用实例
choices
该参数接收一个可迭代的列表或元组(基本单位为二元组)。如果指定了该参数,在实例化该模型时,该字段只能取选项列表中的值。
一个选项列表:
YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),)
每个二元组的第一个值’FR’会储存在数据库中,而第二个值’Freshman’将只会用于显示作用。
完整代码示例
class Student(models.Model):
FRESHMAN = 'FR'
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'
YEAR_IN_SCHOOL_CHOICES = (
(FRESHMAN, 'Freshman'),
(SOPHOMORE, 'Sophomore'),
(JUNIOR, 'Junior'),
(SENIOR, 'Senior'),
)
year_in_school = models.CharField(
max_length=2,
choices=YEAR_IN_SCHOOL_CHOICES,
default=FRESHMAN,
)
虽然您可以在模型类之外定义一个选择列表,然后再引用它,但是在模型类中定义每个选项的选择和名称会将所有这些信息保存在使用它的类中,并使这些选择易于引用
还可以将可用的选择收集到指定的组中,这些组可用于组织目的:
MEDIA_CHOICES = (
('Audio', (
('vinyl', 'Vinyl'),
('cd', 'CD'),
)
),
('Video', (
('vhs', 'VHS Tape'),
('dvd', 'DVD'),
)
),
('unknown', 'Unknown'),)
对于一个模型实例,要获取该字段二元组中相对应的第二个值,使用 get_FOO_display() 方法。例如:
from django.db import models
class Person(models.Model):
SHIRT_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size'L'
>>> p.get_shirt_size_display()
'Large'
default
-
作用:字段的默认值。这可以是一个值,也可以是一个可调用的对象。如果是可调用的,则每次创建新对象时都会调用它。当创建新的模型实例并且没有为该字段提供值时,将使用默认值。当字段是主键时,当字段设置为None.
-
注意:
- 默认值不能是可变对象(模型实例,list, set,作为对该对象的同一个实例的引用,将在所有新的模型实例中用作默认值。相反,将所需的默认值包装为可调用的。例如,如果要指定默认值dict为JSONField,使用以下功能:
def contact_default():
return {"email": "to1@example.com"}
contact_info = JSONField("ContactInfo", default=contact_default)
lambdas不能用于字段选项,如default因为他们不可能按迁移序列化…请参阅文档中的其他注意事项。对于像这样的领域ForeignKey该映射到模型实例,默认值应该是它们引用的字段的值(pk除非to_field而不是模型实例。当创建新的模型实例并且没有为该字段提供值时,将使用默认值。当字段是主键时,当字段设置为None.
help_text
与表单小部件一起显示的额外“帮助”文本。即使您的字段没有在表单上使用,它对于文档也很有用。请注意,此值为不HTML-以自动生成的形式转义。这使您可以将HTML包含在help_text如果你如此渴望。例如:
help_text="Please use the following format: <em>YYYY-MM-DD</em>."
或者,您可以使用纯文本和django.utils.html.escape()
以转义任何HTML特殊字符。确保您避免了来自不受信任用户的任何帮助文本,以避免跨站点脚本攻击。
二、类型字段
2.1、字段枚举介绍
字段 | 描述 | 备注说明 |
---|---|---|
IntegerField | 整型字段 | a、用于保存一个整数 |
BigIntegerField | 整形字段 | a、一个64位整数 |
AutoField | 自增字段 | a、一个根据实际ID自动增长的IntegerField |
BigAutoField | 自增字段 | a、一个64位整数 |
FloatField | 浮点数字段 | a、用Python的float实例来表示的浮点数 |
DecimalField | 十进制浮点数字段 | a、使用python的Decimal实例表示的十进制浮点数;b、参数额外必须(max_digits、decimal_places) |
CharField | 字符字段 | a、默认的表单样式是TextInput;b、参数额外必须 max_length; |
TextField | 大文本字段 | a、一般超过4000使用;b、默认的表单控件是Textarea |
EmailField | 邮件格式字段 | |
BinaryField | 二进制数据字段 | a、使用Python的bytes, bytearray或memoryview实例;b、默认情况下设置editable为False;c、支持额外可选参数max_length |
BooleanField | 布尔字段 | true/false 字段此字段的默认表单控制是CheckboxInput |
NullBooleanField | 布尔空字段 | 支持null、true、false三种值 |
DateField | 日期字段 | a、使用Python的datetime.date实例表示的日期;b、参数额外可选支持(auto_now、auto_now_add) |
TimeField | 时间字段 | a、使用Python的datetime.time实例表示的时间;b、参数额外可选支持(auto_now、auto_now_add) |
DateTimeField | 日期时间字段 | a、使用Python的datetime.datetime实例表示的日期和时间;b、参数额外可选支持(auto_now、auto_now_add) |
URLField | URL字段 | |
FileField | 文件字段 | 一个上传文件的字段 |
ImageField | 图片字段 | 继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image |
2.2、字段使用实例
BinaryField
方法:classBinaryField(max_length = None,** options)
作用:用于存储原始二进制数据的字段。它可以分配bytes, bytearray或memoryview。 默认情况下,BinaryField设置editable为False,在这种情况下,它不能包含在a中ModelForm。在Django 2.1中更改:较旧的版本不允许设置editable为True。
有一个额外的可选参数:
参数:
- BinaryField.max_length:字段的最大长度(以字符为单位)。在Django的验证中强制使用最大长度 MaxLengthValidator。
DecimalField
方法:DecimalField(max_digits =无,decimal_places =无,**kwargs)
作用:一个固定精度的十进制数,由Python Decimal实例表示。它使用验证输入 DecimalValidator。有两个必需的参数:
参数:
- max_digits : 数字中允许的最大位数。请注意,此数字必须大于或等于decimal_places
- decimal_places:与数字一起存储的小数位数。
例如,要存储999分辨率为2位小数的数字,您可以使用:
models.DecimalField(..., max_digits=5, decimal_places=2)
并存储大约10亿的数字,分辨率为10位小数:
models.DecimalField(..., max_digits=19, decimal_places=10)
此字段的默认表单控件是NumberInput 当localize为False或 TextInput以其他方式。
常见问题:
- decimal.InvalidOperation
- 错误提示:
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]
- 错误解释:这个报错其实是数据库的数据长度超过了原本的长度,因为max_digits的长度包括decimal_places的长度,数据库的数据经过运算超越了原本的最大长度,就会出现这种报错!
- 解决办法:修改对应model字段中max_digits的值,长度增加,就能解决这个异常!
- 错误提示:
DurationField
方法:DurationField(** options)
作用:用于存储时间段的字段 - 用Python建模 timedelta。在PostgreSQL上使用时,使用interval的数据类型是Oracle,数据类型是。否则使用一微秒。INTERVAL DAY(9) TO SECOND(6)bigint
EmailField
方法:EmailField(max_length = 254,** options)
作用:检查该值是使用一个有效的电子邮件地址 EmailValidator。
FileField
方法:FileField(upload_to = None,max_length = 100,** options)
作用:文件上传字段。
参数:
- 该primary_key参数不受支持,如果使用则会引发错误。
- upload_to:可选,此属性提供了设置上载目录和文件名的方法,可以通过两种方式进行设置。在这两种情况下,该值都将传递给该
Storage.save()
方法。如果指定字符串值,则可能包含strftime()
格式,该格式将替换为文件上载的日期/时间(以便上载的文件不会填满给定目录)。例如:
class MyModel(models.Model):
# file will be uploaded to MEDIA_ROOT/uploads
upload = models.FileField(upload_to='uploads/')
# or...
# file will be saved to MEDIA_ROOT/uploads/2015/01/30
upload = models.FileField(upload_to='uploads/%Y/%m/%d/')
补充:
- 如果使用默认值 FileSystemStorage,则字符串值将附加到MEDIA_ROOT路径中,以在本地文件系统上形成将存储上载文件的位置。
- 如果您使用的是其他存储,请检查该存储的文档以了解其处理方式upload_to。upload_to也可以是可调用的,例如函数。这将被调用以获取上载路径,包括文件名。这个callable必须接受两个参数并返回一个Unix风格的路径(带有正斜杠)以传递给存储系统。如下示例
def user_directory_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
return 'user_{0}/{1}'.format(instance.user.id, filename)
class MyModel(models.Model):
upload = models.FileField(upload_to=user_directory_path)
代码说明:
- instance:FileField定义模型的实例 。更具体地说,这是附加当前文件的特定实例。在大多数情况下,此对象尚未保存到数据库中,因此如果它使用默认值AutoField,则可能还没有其主键字段的值。
- filename:最初提供给文件的文件名。在确定最终目的地路径时可能会或可能不会考虑这一点。
- storage:可选,存储对象,用于处理文件的存储和检索。此字段的默认表单窗口小部件是ClearableFileInput。
在模型中使用 FileField或ImageField(见下文)需要几个步骤:
- 在您的设置文件中,您需要定义MEDIA_ROOT为您希望Django存储上载文件的目录的完整路径。(为了提高性能,这些文件不会存储在数据库中。)定义 MEDIA_URL为该目录的基本公共URL。确保Web服务器的用户帐户可以写入此目录。
- 将FileField或添加ImageField到模型中,定义upload_to用于指定MEDIA_ROOT用于上载文件的子目录的选项。
- 将存储在数据库中的所有内容都是文件的路径(相对于MEDIA_ROOT)。你很可能想要使用urlDjango提供的便利属性。例如,如果ImageField调用了 mug_shot您,则可以在模板中获取图像的绝对路径 。{{object.mug_shot.url }}
例如,假设您MEDIA_ROOT设置为’/home/media’,并 upload_to设置为’photos/%Y/%m/%d’。所述’%Y/%m/%d’ 的部分upload_to被strftime()格式化; ‘%Y’是四位数的年份,’%m’是两位数的月份,’%d’是两位数的一天。如果您在2007年1月15日上传文件,它将保存在目录中/home/media/photos/2007/01/15。如果要检索上载文件的磁盘文件名或文件大小,可以分别使用name和 size属性; 有关可用属性和方法的更多信息,请参阅File类引用和管理文件 主题指南。
FilePathField
方法:FilePathField(path = None,match = None,recursive = False,max_length = 100,** options)
作用:CharField的选择仅限于文件系统上某个目录中的文件名。有三个特殊参数,其中第一个是 必需的:
参数:
- path:必须。从中可以FilePathField选择的目录的绝对文件系统路径 。示例:"/home/images"。
- match:可选的。作为字符串的正则表达式,用于过滤文件名。请注意,正则表达式将应用于基本文件名,而不是完整路径。例如:
"foo.*\.txt$"
,这将匹配一个名为foo23.txt而不是bar.txt或foo23.png。 - recursive:可选的。无论是True或False。默认是False。指定是否path应包括所有子目录
- allow_files:可选的。无论是True或False。默认是True。指定是否应包含指定位置的文件。无论是这个是 allow_folders必须的True。
- allow_folders:可选的。无论是True或False。默认是False。指定是否应包括指定位置的文件夹。无论是这个还allow_files必须的True。
- max_length:可选的。实例在数据库中创建为varchar 默认最大长度为100个字符的列。与其他字段一样,您可以使用max_length参数更改最大长度。
当然,这些参数可以一起使用。一个潜在的问题是match适用于基本文件名,而不是完整路径。所以,这个例子:
FilePathField(path="/home/images", match="foo.*", recursive=True)
ImageField
方法:ImageField(upload_to = None,height_field = None,width_field = None,max_length = 100,** options)
作用:从中继承所有属性和方法FileField,但也验证上载的对象是有效图像。除了可用于特殊属性FileField,一个ImageField也具有height和width属性。为了便于查询这些属性,ImageField有两个额外的可选参数:
参数:
- height_field:可选,每次保存模型实例时,将使用图像高度自动填充的模型字段的名称。
- width_field:可选,每次保存模型实例时,将使用图像宽度自动填充的模型字段的名称。
GenericIPAddressField
方法:GenericIPAddressField(protocol ='both',unpack_ipv4 = False,** options)
作用:IPv4或IPv6地址,采用字符串格式(例如192.0.2.30或 2a02:42fe::4)。此字段的默认表单窗口小部件是aTextInput。
参数:
- protocol:必选,限制指定协议的有效输入。可接受的值是’both’(默认),‘IPv4’ 或’IPv6’。匹配不区分大小写。
- unpack_ipv4:必选,解压缩IPv4映射地址,如::ffff:192.0.2.1。如果启用此选项,则该地址将被解压缩到 192.0.2.1。默认为禁用。只能在protocol设置为时使用’both’。
如果允许空值,则必须允许空值,因为空值存储为空。
UUIDField
方法:UUIDField(** options)
作用:用于存储通用唯一标识符的字段。使用Python的 UUID类。在PostgreSQL上使用时,它以uuid数据类型存储,否则存储在char(32)。通用唯一标识符是AutoFieldfor的 一个很好的替代品primary_key。数据库不会为您生成UUID,因此建议使用default:
import uuidfrom django.db import models
class MyUUIDModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# other fields
请注意,可调用(省略括号)将传递给default,而不是实例UUID。
三、关系字段
3.1、字段枚举介绍
字段 | 描述 | 备注说明 |
---|---|---|
ForeignKey | 外键字段(一对多字段) | 一对多,将字段定义在多的端中注意:你需要把被引用的类定义在前面,否则变量无法在这里是有。不过你可以用字符串来代替,如果累是在同一个文件里定义的,那就只需要类名,否则的话就需要用点激活比如(‘myapp.TEventType’) |
ManyToManyField | 多对多字段 | 定义一个多对多的关联关系,将字段定义在两端中 |
OneToOneField | 一对一字段 | 将字段定义在任意一端中 |
4.2、字段使用实例
ForeignKey
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
OneToOneField
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name=models.CharField( max_length=32)
age=models.IntegerField()
# 与AuthorDetail建立一对一的关系
authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)
ManyToManyField
a、定义普通多对多关系
class Book(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField( max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2)
# 与Publish建立一对多的关系,外键字段建立在多的一方
publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
authors=models.ManyToManyField(to='Author',)
b、定义额外字段的多对多关系
有的时候你可能会需要在两个模型的关系中记录更多的数据。对于这些情况,Django允许你指定用于控制多对多关系的模型。你可以在中间模型当中添加而外的字段。在实例化 ManyToManyField 的时候使用 through 参数指定多对多关系使用哪个中间模型。对于我们举的音乐家的例子,代码如下:
class Article(models.Model):
"""
文章
"""
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=50, verbose_name='文章标题')
desc = models.CharField(max_length=255, verbose_name='文章描述')
content = models.TextField(verbose_name='文章内容')
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
comment_count = models.IntegerField(default=0)
like_count = models.IntegerField(default=0)
unlike_count = models.IntegerField(default=0)
user = models.ForeignKey(verbose_name='作者', to='APP_User.UserInfo', to_field='id', on_delete=models.CASCADE)
category = models.ForeignKey(to='Category', to_field='id', null=True, on_delete=models.CASCADE)
tags = models.ManyToManyField(
to="Tag",
through='Article2Tag',
through_fields=('article', 'tag'),
)
def __str__(self):
return self.title
class Article2Tag(models.Model):
id = models.AutoField(primary_key=True)
article = models.ForeignKey(verbose_name='文章', to="Article", to_field='id', on_delete=models.CASCADE)
tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='id', on_delete=models.CASCADE)
class Meta:
unique_together = [
('article', 'tag'),
]
def __str__(self):
v = self.article.title + "---" + self.tag.title
return v
四、元类字段
4.1、字段枚举介绍
三、元选项
什么是元选项:在模型类中定义Meta类,用于设置元信息
以下是自己比较常用的,
属性 | 作用描述与注意事项 | 使用实例 |
---|---|---|
db_table | 定义数据表名 | a、推荐使用小写字母,数据表名默认为项目名小写_类名小写db_table = ‘music_album’ |
ordering | 对象的默认排序字段 | 获取对象的列表时使用注意:排序会增加数据库的开销 ,要按照pub_date字段的正序排序,这样写:ordering = [‘pub_date’]按照pub_date字段的倒序排序,这样写:ordering = [’-pub_date’]先按照pub_date的倒序排序,再按照 author 的正序排序,这样写:ordering = [’-pub_date’, ‘author’] |
abstract | 如果 abstract = True, 就表示模型是 抽象基类 (abstract base class) | |
get_latest_by | 模型中某个可排序的字段的名称,比如DateField、DateTimeField或者IntegerField。它指定了Manager的latest()和earliest()中使用的默认字段 | get_latest_by = “order_date” |
permissions | 添加授权 | |
unique_together | 一起使用的字段名称集必须是唯一的 | |
4.2、字段使用实例
db_table
class TPriorityDimension(models.Model):
weight_ratio = models.IntegerField(default=10)
class Meta:
db_table = 't_priority_dimension'
ordering = ['-weight_ratio']
五、类方法
5.1、添加模型的字符串表示形式
__str__()
方法
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
def __str__(self):
return self.name
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField()
def __str__(self):
return u'%s %s' % (self.first_name, self.last_name)
六、补充
6.1、字段命名限制
Django在模型字段命名方面仅有两个限制。
- 一个字段的名称不能是Python保留字,因为这回导致Python语法错误。比如:
class Example(models.Model):
pass = models.IntegerField() # 'pass' is a reserved word!
- 一个字段名称不能包含连续的多个下划线,原因在于Django查询语法的工作方式。比如:
class Example(models.Model):
foo__bar = models.IntegerField() # 'foo__bar' has two underscores!