django入门【05】模型介绍(二)——字段选项

文章目录

    • [1、null 和 blank](#1、null 和 blank)
      • 示例
      • 说明
      • [⭐ `null` 和 `blank` 结合使用的几种情况总结:](#⭐ nullblank 结合使用的几种情况总结:)
    • 2、choices
      • [**`choices` 在 Django 中有以下几种形式:**](#choices 在 Django 中有以下几种形式:)
        • [(1) **简单的列表或元组形式**](#(1) 简单的列表或元组形式)
        • [(2) **字典映射形式**](#(2) 字典映射形式)
        • [(3) **分组形式**](#(3) 分组形式)
        • [(4) **可调用对象**](#(4) 可调用对象)
        • (5)**枚举类型**
        • [(6) **自定义数据类型的枚举**](#(6) 自定义数据类型的枚举)
    • [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 或默认时,留空会存入空字符串 ''

nullblank 结合使用的几种情况总结:

null=False null=True
blank=False 数据库中不可为 NULL; 表单中必填 数据库中可为 NULL; 表单中必填
blank=True 数据库中不可为 NULL; 表单中可不填; ⭐ 留空时存入 '' 数据库中可为 NULL; 表单中可不填; ⭐ 留空时存入 NULL

通过合理使用 nullblank 的组合,可以根据需求控制数据库和表单的行为。

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 的 TextChoicesIntegerChoices 等枚举类来定义 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 与特定数据类型(如 datedatetime)结合,适合需要特定类型支持的场景,如日期 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 新增):在数据库层面设置字段的默认值,可以是常量或数据库函数。注意,不能引用其他字段或模型。

    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):    
        # 设置数据库默认值:使用数据库函数
        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:设置字段的默认值,可以是固定值或动态生成的值。对于不可变对象,直接使用;对于可变对象,必须使用函数。

    python 复制代码
    from 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

    python 复制代码
    class 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 :指定字段验证时的错误信息,接受 nullblankinvalidunique 等键值字典,覆盖默认错误消息。

    python 复制代码
    from 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,但需注意转义。

    python 复制代码
    from 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=Falseunique=True。Django 默认会创建一个主键字段,除非手动指定。

  • unique :若为 True,则字段值必须在整个表中唯一。数据库和模型验证都会强制执行。

  • unique_for_dateunique_for_monthunique_for_year :限定字段值在指定日期、月份或年份内唯一。适用于日期和时间字段,依赖 Model.validate_unique() 验证。

    举例 :假设我们有一个新闻文章模型 Article,其中每篇文章的标题在同一发布日期内必须唯一。通过 unique_for_dateunique_for_monthunique_for_year 来实现这一需求。

    python 复制代码
    from 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 的模型层面生效,在数据库层面不会强制执行。

    • 需要日期字段支持 :这些约束依赖于 DateFieldDateTimeField 类型的日期字段,必须在同一个模型中定义。

    • 仅在 ORM 中生效:这些唯一性约束只在 Django ORM 中保存数据时有效,直接在数据库中插入数据时不会验证。

    • 不适用于被表单排除的字段:如果日期字段在表单中被排除或不可编辑,Django 无法验证该唯一性约束。常见的"排除"方式有以下几种:

      • ①在表单的 exclude 选项中指定字段

        python 复制代码
        from 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 中仅指定需要的字段

        python 复制代码
        class ArticleForm(forms.ModelForm):
            class Meta:
                model = Article
                fields = ['title', 'content']  # 仅包括 'title' 和 'content' 字段
      • ③ 将字段设为不可编辑(editable=False

        python 复制代码
        from 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"。

    python 复制代码
    from 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:使用正则表达式检查字段格式。

    示例代码:

    python 复制代码
    from 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 异常。自定义验证器可以实现更加灵活、复杂的规则。

    示例代码:

    python 复制代码
    from 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)
            ]  
        )
相关推荐
长栎3 分钟前
写 for 循环写了十年,你却从没用过迭代器模式最狠的那一面
后端
LiaCode7 分钟前
Redis 在生产项目的使用
前端·后端
用户5598224812211 分钟前
Docker Compose Down 导致容器数据误删——ext4 日志恢复全记录
后端
LiaCode12 分钟前
一天学完 redis 的爽翻版核心知识总结
前端·后端
大刚测试开发实战13 分钟前
如何内网穿透访问本地私有化部署的TestHub
前端·后端·github
xiaodaoluanzha32 分钟前
迄今為止,最簡單的編程語言 Nolang
前端·后端
Csvn32 分钟前
Docker 容器管理入门 — 从镜像到容器编排
后端
用户7623524259137 分钟前
ShardingJDBC
后端
行者全栈架构师38 分钟前
IDEA 中 Maven 项目的 15 个红色报错快速解决方法
java·后端
Colin草率地做慢慢地改42 分钟前
关于QuickStore这个项目的重构(2)- 数据库建表文件
后端·面试·架构