文章目录
-
- [1、null 和 blank](#1、null 和 blank)
- 2、choices
-
- [**`choices` 在 Django 中有以下几种形式:**](#
choices
在 Django 中有以下几种形式:) -
- [(1) **简单的列表或元组形式**](#(1) 简单的列表或元组形式)
- [(2) **字典映射形式**](#(2) 字典映射形式)
- [(3) **分组形式**](#(3) 分组形式)
- [(4) **可调用对象**](#(4) 可调用对象)
- (5)**枚举类型**
- [(6) **自定义数据类型的枚举**](#(6) 自定义数据类型的枚举)
- [**`choices` 在 Django 中有以下几种形式:**](#
- [3. 基本 数据库 相关选项 (db_column、db_comment、db_default、db_index、db_tablespace)](#3. 基本 数据库 相关选项 (db_column、db_comment、db_default、db_index、db_tablespace))
- [4. 默认值与编辑选项(default、editable)](#4. 默认值与编辑选项(default、editable))
- [5. 错误消息与帮助文本(error_messages、help_text)](#5. 错误消息与帮助文本(error_messages、help_text))
- [6. 唯一性选项(primary_key、unique、unique_for_date、unique_for_month、unique_for_year)](#6. 唯一性选项(primary_key、unique、unique_for_date、unique_for_month、unique_for_year))
- [7. 其他选项(verbose_name、validators、自定义验证器)](#7. 其他选项(verbose_name、validators、自定义验证器))
1、null 和 blank
示例
python
class NullOrBlank(models.Model):
null_demo = models.CharField(null=True, max_length=200)
blank_demo = models.CharField(blank=True, max_length=200)
null_or_blank_demo = models.CharField(null=True, blank=True, max_length=200)
说明
null
:默认为False
;设为True
时,表示字段在数据库中可以存储NULL
值。blank
:默认为False
;设为True
时,表示在 Django 表单(如 Admin 后台)中可以留空。
当 blank=True
时,存入数据库的值依赖于 null
的设置:
null=True
时,留空会存入NULL
。null=False
或默认时,留空会存入空字符串''
。
⭐ null
和 blank
结合使用的几种情况总结:
null=False |
null=True |
|
---|---|---|
blank=False |
数据库中不可为 NULL ; 表单中必填 |
数据库中可为 NULL ; 表单中必填 |
blank=True |
数据库中不可为 NULL ; 表单中可不填; ⭐ 留空时存入 '' |
数据库中可为 NULL ; 表单中可不填; ⭐ 留空时存入 NULL |
通过合理使用 null
和 blank
的组合,可以根据需求控制数据库和表单的行为。
2、choices
choices
是一个映射或可迭代对象,用作模型字段的选项。
提供 choices
后,字段的默认表单小部件会变为选择框,并且选项会被模型验证强制执行。
choices
在 Django 中有以下几种形式:
(1) 简单的列表或元组形式
这是最常见的形式,choices
定义为一个包含 (值, 可读名称)
的列表或元组。适用于定义一组固定的选项。
python
# 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",
)
(2) 字典映射形式
在 Django 5.0 中,choices
支持使用字典定义,每个键表示实际存储的值,值为可读名称。适合需要清晰键值对的场景。
python
# 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",
)
(3) 分组形式
通过嵌套元组或字典分组的方式,将选项分为不同类别,适用于多级选择的场景。
python
# 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",
)
(4) 可调用对象
将 choices
定义为一个无参可调用对象(如函数),函数返回一个合法的 choices
结构(例如列表或字典)。适合动态生成选项的场景。
python
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)
(5)枚举类型
使用 Django 的 TextChoices
、IntegerChoices
等枚举类来定义 choices
,适合需要常量化、易读性高的选择项。Django 自动生成 .choices
、.labels
、.values
等属性,便于管理。
python
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)
(6) 自定义数据类型的枚举
在 Choices
类的基础上扩展,可以将 choices
与特定数据类型(如 date
或 datetime
)结合,适合需要特定类型支持的场景,如日期 date
python
import datetime
class MoonLandings(datetime.date, models.Choices):
APOLLO_11 = 1969, 7, 20, "Apollo 11 (Eagle)"
APOLLO_12 = 1969, 11, 19, "Apollo 12 (Intrepid)"
这里仅介绍了处理 date
类型的choices
,关于其他自定义数据类型的枚举,请看本专栏文章【 关于Django 模型字段 choices
自定义数据类型的枚举------补充】
综上,Django 中的 choices
形式主要有以上 6 种。
3. 基本 数据库 相关选项 (db_column、db_comment、db_default、db_index、db_tablespace)
db_column
:指定字段在数据库中的列名,默认使用字段名。db_comment
:为数据库列添加注释,方便有数据库访问权限但无 Django 代码访问权限的用户了解字段含义。
db_column 、db_comment 举例:
python
from django.db import models
from django.utils import timezone
from django.db.models import F
from django.db.models.functions import Now, TruncMonth
from datetime import timedelta
class Article(models.Model):
title = models.CharField(
max_length=200,
db_column="article_title", # 自定义数据库列名,如果不指定 db_column,列名将默认使用字段名,也就是 title。
db_comment="文章标题", # 数据库列注释
)
-
db_default
(Django 5.0 新增):在数据库层面设置字段的默认值,可以是常量或数据库函数。注意,不能引用其他字段或模型。pythonfrom django.db import models from django.utils import timezone from django.db.models import F from django.db.models.functions import Now, TruncMonth from datetime import timedelta class Article(models.Model): # 设置数据库默认值:使用数据库函数 created_at = models.DateTimeField( db_default=Now(), # 使用数据库层面的 Now 函数作为默认值 ) # 设置数据库默认值:使用复杂的数据库表达式-->设置为"当前日期" 90 天后的月份的第一天 due_date = models.DateField( db_default=TruncMonth(Now() + timedelta(days=90), output_field=models.DateField()) )
-
db_index
:设置为True
时,创建数据库索引。建议使用Meta.indexes
替代。 -
db_tablespace
:用于指定字段索引存放的数据库表空间。简单来说,它可以将字段的索引存储在特定的物理位置,从而实现更高效的数据库管理和性能优化。此选项仅在支持表空间的数据库(如 PostgreSQL、Oracle)中有效。
db_index 、db_tablespace 举例:
python
from django.db import models
from django.utils import timezone
from django.db.models import F
from django.db.models.functions import Now, TruncMonth
from datetime import timedelta
class Article(models.Model):
# 设置字段的索引,且使用表空间
pub_date = models.DateTimeField(
db_index=True, # 为字段创建数据库索引
db_tablespace="custom_tablespace" # 使用自定义表空间
)
4. 默认值与编辑选项(default、editable)
-
default
:设置字段的默认值,可以是固定值或动态生成的值。对于不可变对象,直接使用;对于可变对象,必须使用函数。pythonfrom django.db import models from django.utils import timezone class ExampleModel(models.Model): # 1. 固定值:使用固定值作为默认值 status = models.CharField( max_length=20, default="Draft" # 设置固定字符串作为默认值 ) # 2. 函数/可调用对象:使用函数或或可调用对象生成动态默认值(例如当前时间) created_at = models.DateTimeField( default=timezone.now # 使用当前时间作为默认值 ) # 3. 不可变和可变对象:对不可变对象可以直接使用; # 对于可变对象(如列表或字典),必须使用函数包装来避免所有实例共享同一对象。 settings = models.JSONField( default=lambda: {"theme": "light", "notifications": True} # 使用函数生成默认值 )
-
editable
:决定字段是否在表单和管理后台中显示。对于只读或自动生成的字段,通常设为False
。pythonclass Article(models.Model): # 1. 默认(editable=True):该字段会出现在管理后台和表单中,可以编辑。 title = models.CharField( max_length=200, default="Untitled Article", # 默认值,可编辑 editable=True # 可以编辑 ) # 2. 设为不可编辑,该字段不会出现在管理后台或 ModelForm 表单中,也不会进行表单验证,通常用于只读或自动生成的字段。 description = models.TextField( default="No description provided.", # 默认值,不可编辑 editable=False # 不会出现在表单中 )
5. 错误消息与帮助文本(error_messages、help_text)
-
error_messages
:指定字段验证时的错误信息,接受null
、blank
、invalid
、unique
等键值字典,覆盖默认错误消息。pythonfrom django.db import models class UserProfile(models.Model): # 用户名字段自定义错误消息 username = models.CharField( max_length=50, unique=True, # 设置唯一性约束 """ blank:当表单提交时,若 username 为空,返回"用户名不能为空,请输入您的用户名。"。 unique:若 username 值重复,返回"此用户名已被使用,请选择其他用户名。"。 null:当数据库层面该字段值为 NULL 时,返回"用户名不能为空值。"。 invalid:若 username 格式不符合要求(例如设置了其他验证器),返回"用户名格式不正确,请检查输入。"。 """ error_messages={ 'blank': "用户名不能为空,请输入您的用户名。", 'unique': "此用户名已被使用,请选择其他用户名。", 'null': "用户名不能为空值。", 'invalid': "用户名格式不正确,请检查输入。" } ) # 邮箱字段自定义错误消息 email = models.EmailField( """ 若邮箱为空,提示"邮箱不能为空,请输入您的邮箱地址。"。 若邮箱格式不符合要求,提示"邮箱格式不正确,请输入有效的邮箱地址。"。 """ error_messages={ 'blank': "邮箱不能为空,请输入您的邮箱地址。", 'invalid': "邮箱格式不正确,请输入有效的邮箱地址。" } )
-
help_text
:提供额外的帮助文本,通常用于表单中。可以使用 HTML,但需注意转义。pythonfrom django.db import models class Product(models.Model): name = models.CharField( max_length=100, help_text="请输入产品名称,不超过100个字符。" )
6. 唯一性选项(primary_key、unique、unique_for_date、unique_for_month、unique_for_year)
-
primary_key
:若为True
,则该字段作为模型的主键。主键字段默认为null=False
和unique=True
。Django 默认会创建一个主键字段,除非手动指定。 -
unique
:若为True
,则字段值必须在整个表中唯一。数据库和模型验证都会强制执行。 -
unique_for_date
、unique_for_month
、unique_for_year
:限定字段值在指定日期、月份或年份内唯一。适用于日期和时间字段,依赖Model.validate_unique()
验证。举例 :假设我们有一个新闻文章模型
Article
,其中每篇文章的标题在同一发布日期内必须唯一。通过unique_for_date
、unique_for_month
和unique_for_year
来实现这一需求。pythonfrom django.db import models class Article(models.Model): title = models.CharField(max_length=200) pub_date = models.DateField() # 发布日期字段 # 设置 title 在特定时间段内唯一 unique_for_day_title = models.CharField( max_length=200, unique_for_date="pub_date", # 在同一天内,title 必须唯一 ) unique_for_month_title = models.CharField( max_length=200, unique_for_month="pub_date", # 在同一个月内,title 必须唯一 ) unique_for_year_title = models.CharField( max_length=200, unique_for_year="pub_date", # 在同一年内,title 必须唯一 )
注意:
-
非数据库层面:这些约束仅在 Django 的模型层面生效,在数据库层面不会强制执行。
-
需要日期字段支持 :这些约束依赖于
DateField
或DateTimeField
类型的日期字段,必须在同一个模型中定义。 -
仅在 ORM 中生效:这些唯一性约束只在 Django ORM 中保存数据时有效,直接在数据库中插入数据时不会验证。
-
不适用于被表单排除的字段:如果日期字段在表单中被排除或不可编辑,Django 无法验证该唯一性约束。常见的"排除"方式有以下几种:
-
①在表单的
exclude
选项中指定字段pythonfrom django import forms from .models import Article class ArticleForm(forms.ModelForm): class Meta: model = Article fields = ['title', 'content'] # 只包含 'title' 和 'content' exclude = ['pub_date'] # 排除 'pub_date' 字段
-
② 在
fields
中仅指定需要的字段pythonclass ArticleForm(forms.ModelForm): class Meta: model = Article fields = ['title', 'content'] # 仅包括 'title' 和 'content' 字段
-
③ 将字段设为不可编辑(
editable=False
)pythonfrom django.db import models class Article(models.Model): title = models.CharField(max_length=200) content = models.TextField() pub_date = models.DateField(editable=False) # 设置为不可编辑
-
-
7. 其他选项(verbose_name、validators、自定义验证器)
-
verbose_name
:字段的可读名称,若不设置,Django 会自动使用字段名(将下划线转换为空格)。例如,first_name
字段默认会显示为 "First name"。pythonfrom django.db import models class Product(models.Model): # 设置了 verbose_name name = models.CharField( max_length=100, verbose_name="产品名称" # 字段显示为"产品名称" ) # 未设置 verbose_name,Django 使用字段名,自动替换下划线为空格 product_code = models.CharField(max_length=20) # 显示为"Product code" # 使用 gettext_lazy 进行国际化 description = models.TextField( verbose_name=_("Product Description") # 支持多语言的字段名 )
注 :关于 gettext_lazy 国际化,欢迎查看【Django 启用国际化支持---实现配置多国语言-CSDN博客】
-
validators
:字段的验证器列表,可包含自定义的验证函数或预定义的验证器。Django 提供了一些常用的内置验证器,常见的有:
MaxValueValidator
:检查值是否不超过指定的最大值。MinValueValidator
:检查值是否不低于指定的最小值。MaxLengthValidator
:检查文本字段的最大长度。MinLengthValidator
:检查文本字段的最小长度。EmailValidator
:检查字段是否是有效的电子邮件地址。URLValidator
:检查字段是否是有效的 URL 地址。RegexValidator
:使用正则表达式检查字段格式。
示例代码:
pythonfrom django.db import models from django.core.validators import MaxValueValidator, MinValueValidator, EmailValidator, RegexValidator class UserProfile(models.Model): # 年龄字段,要求在 0 到 120 之间 age = models.IntegerField( validators=[MinValueValidator(0), MaxValueValidator(120)] ) # 邮箱字段,验证邮箱格式 email = models.EmailField( validators=[EmailValidator(message="请输入有效的邮箱地址")] ) # 手机号字段,使用正则表达式验证手机号格式 phone_number = models.CharField( max_length=15, validators=[RegexValidator(regex=r'^\+?1?\d{9,15}$', message="请输入有效的手机号")] )
自定义验证器示例
自定义验证器是一个函数或可调用对象,它接收一个值作为参数并验证该值。如果验证失败,需要抛出
ValidationError
异常。自定义验证器可以实现更加灵活、复杂的规则。示例代码:
pythonfrom django.core.exceptions import ValidationError from django.core.validators import MinLengthValidator, MaxLengthValidator import re # 自定义验证器函数 """ 假设我们想确保用户名不能包含特殊字符,且长度限制在 5 到 30 个字符之间,可以编写一个自定义验证器: """ def validate_username(value): if not re.match(r'^[a-zA-Z0-9_]+$', value): raise ValidationError("用户名只能包含字母、数字和下划线") class User(models.Model): username = models.CharField( max_length=30, validators=[ validate_username, # 应用自定义验证器 MinLengthValidator(5), MaxLengthValidator(30) ] )