Django基础教程(三十九)Django数据模型的设计与实现之字段参数:别瞎填了!Django模型字段参数,是你的数据库结构声明式骚操作

嘿,Django开发者们,有没有这样一种经历:你吭哧吭哧定义好了一个模型,python manage.py makemigrations + migrate 一气呵成,感觉自己是代码界最靓的仔。结果,项目跑着跑着,数据库里冒出一堆null的鬼魂,或者用户邮箱重复注册到天荒地老,又或者日期字段给你来个“千年虫”预演。

然后你开始疯狂写form验证,在view里写一堆if...else来判断,搞得代码又臭又长。停!快住手!问题的根源,很可能在于你当初设计模型时,忽略了那些看似不起眼,实则法力无边的字段参数!

今天,咱们就来一场深度“扒皮”,看看这些参数到底有多能打。

第一章:参数界的“基础生存包”——不写会出人命的那种

这些参数,属于那种你如果不设置,Django就会用“最宽容”(也最危险)的方式对待你的数据。但真正的狠人,从不把决定权交给默认值。

  1. null:数据库的“空位”许可证
    • 口语化解读:这个字段在数据库里,允许存NULL不?True就是允许,False就是不允许。这玩意儿是跟数据库直接对话的。
    • 潜规则强烈建议,对于字符型字段(CharField, TextField),优先设置null=False 为啥?因为Django的惯例是,如果一个字符串字段没值,就用空字符串'',而不是NULL。这样能避免数据库里出现两种“空”(NULL'')的哲学难题。如果你看到一个CharField设置了null=True,心里要咯噔一下,这哥们可能有点故事。
    • 示例bio = models.TextField(null=True, blank=True) # 个人简介,可留空,数据库也允许为NULL。
  1. blank:管理员后台的“免填”金牌
    • 口语化解读:在Django自带的Admin后台或者你用ModelForm生成的表单里,这个字段能不能不填?True就是可以不填,False就是必须填。
    • null vs blank 傻傻分不清楚?
      • null数据库层面的约束。
      • blank表单验证层面的约束。
      • 所以它们俩经常成对出现:null=True, blank=True 表示“数据库允许为空,表单也允许不填”。而如果一个字段是null=False, blank=True,那就意味着表单可以不填,但一旦提交,数据库会默认给它塞个值(比如空字符串),绝不会让它成为NULL
  1. default:万能备胎,永不空军
    • 口语化解读:如果创建记录时没给这个字段传值,数据库就会自动把这个“备胎”转正。它可以是静态值,也可以是一个可调用对象(比如函数)。
    • 骚操作:用datetime.now而不是datetime.now()。因为now()会在模型定义时就被执行,时间就固定死了。而now是个函数,会在每次创建新记录时被执行。
    • 示例
from django.utils import timezone
created_time = models.DateTimeField(default=timezone.now) # 注意是timezone.now,不是timezone.now()
is_active = models.BooleanField(default=True) # 新用户默认激活
  1. choices:给你的字段一个“选择题库”
    • 口语化解读:这个字段的值,不能随便瞎写,必须从你给的选项里选。这不仅是表单层面会变成下拉框,在数据库层面也是一种约束(虽然数据库实现方式不同,但Django会帮你校验)。
    • 最佳实践:一定要用二维元组的二元组,并且选项值用全大写常量,显得专业!
    • 示例
class UserProfile(models.Model):
    GENDER_CHOICES = (
        ('M', '猛男'),
        ('F', '萌妹'),
        ('S', '神秘大佬'),
    )
    gender = models.CharField(max_length=1, choices=GENDER_CHOICES, default='S')
    • Bonus:获取显示文字超方便:user.get_gender_display(),妈妈再也不用担心我写if...else了。
第二章:约束天团——专治各种不服和捣乱数据

这组参数是数据完整性的守护神,它们确保进入你数据库的数据,都是“良民”。

  1. unique:独一无二的贵族
    • 口语化解读:全表上下,这个字段的值不允许有任何重复。是防止重复注册、重复条目的终极武器。
    • 示例email = models.EmailField(unique=True) # 经典用法,每个邮箱只能注册一个账号。
  1. db_index:给数据库装上“搜索引擎”
    • 口语化解读:告诉数据库:“哥们,这个字段我经常要查,你给我建个索引,加速一下。” 对于经常用于查询、过滤、排序的字段,加上它,性能提升立竿见影。但别滥用,索引会降低写入速度并占用空间。
    • 示例title = models.CharField(max_length=200, db_index=True) # 文章标题,经常要被搜索。
  1. primary_key:大佬中的大佬
    • 口语化解读:如果你不指定,Django会自动给你加一个叫id的字段当主键。但如果你任性,想用邮箱当主键呢?设置primary_key=True即可。一旦你这么做了,id字段就不会自动创建了。
    • 慎用:除非有非常充分的理由,否则还是让Django管理自增ID吧,省心。
第三章:关系字段的“专属VIP包厢”

当你的模型开始“搞关系”,这些参数就登场了。

  1. on_delete:当“主子”被删时,“奴才”何去何从?
    • 口语化解读:这是ForeignKeyOneToOneField必填参数!不写会报错。它定义了当被关联的对象被删除时,当前这个对象该怎么处理。
    • 核心选项
      • models.CASCADE连坐。主子没了,奴才也跟着殉葬。最常用,但也最危险。
      • models.PROTECT保护。谁敢删这个主子,就抛异常阻止他。用于非常重要的关联。
      • models.SET_NULL设为空。主子没了,奴才的这个字段设为NULL(前提是字段设置了null=True)。
      • models.SET_DEFAULT设为默认值。主子没了,给奴才找个备胎(需要设default)。
      • models.DO_NOTHING摆烂。我啥也不干,数据库可能会报错,不推荐。
第四章:完整示例——来看一个“丰满”的模型

光说不练假把式,让我们把所有参数组合起来,创建一个博客文章模型,让你看看一个“武装到牙齿”的模型长啥样。

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone

class Category(models.Model):
    name = models.CharField(max_length=100, unique=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

class Article(models.Model):
    # 基础生存包
    title = models.CharField(
        max_length=200,
        verbose_name='文章标题',
        help_text='请输入一个吸引眼球的标题'
    )
    body = models.TextField(verbose_name='文章正文')
    
    # 状态选择
    STATUS_CHOICES = (
        ('draft', '草稿'),
        ('published', '已发布'),
        ('archived', '归档'),
    )
    status = models.CharField(
        max_length=10,
        choices=STATUS_CHOICES,
        default='draft',
        db_index=True # 经常需要按状态过滤,所以加索引
    )

    # 关系与约束
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE, # 用户注销,他的文章也全部删除
        related_name='articles' # 反向关系名,以后可以用 user.articles.all() 取文章
    )
    category = models.ForeignKey(
        Category,
        on_delete=models.PROTECT, # 这个分类下有文章,就不允许删除分类!
        null=True,
        blank=True # 文章可以先不分类
    )
    tags = models.ManyToManyField('Tag', blank=True) # 多对多,允许为空

    # 元数据与默认值
    created_time = models.DateTimeField(default=timezone.now, db_index=True)
    updated_time = models.DateTimeField(auto_now=True) # 每次保存自动更新为当前时间
    published_time = models.DateTimeField(null=True, blank=True)
    is_pinned = models.BooleanField(default=False, verbose_name='是否置顶')

    # 唯一性约束
    slug = models.SlugField(max_length=200, unique=True) # 用于生成友好的URL

    class Meta:
        ordering = ['-is_pinned', '-published_time'] # 默认排序:置顶的在前,然后按发布时间倒序
        verbose_name = '文章'
        verbose_name_plural = '所有文章' # 在Admin后台显示的名称

    def __str__(self):
        return self.title

    def save(self, *args, **kwargs):
        # 重写save方法,实现一些自定义逻辑
        if self.status == 'published' and not self.published_time:
            self.published_time = timezone.now()
        super().save(*args, **kwargs)

class Tag(models.Model):
    name = models.CharField(max_length=50, unique=True)

    def __str__(self):
        return self.name
总结

看到没?字段参数根本不是可有可无的“选答题”,而是构建稳健、高效、可维护Django应用的“必答题”。它们是你对数据行为的一种声明,一种“我早就告诉过你该怎么做”的智慧。

下次当你定义模型时,别再models.CharField(max_length=100)就完事儿了。多花一分钟,问问自己:

  • 它能为空吗?(null, blank
  • 它有默认值吗?(default
  • 它的值有限制吗?(choices
  • 它需要是唯一的吗?(unique
  • 它需要被快速查找吗?(db_index
  • 如果它关联别人,别人没了它咋办?(on_delete

把这些都想明白了,你的Django水平就从“能用”进阶到了“会玩”。你的代码将不再是“跑起来就行”,而是“跑得稳如老狗”。

现在,就去检查一下你的模型吧,看看有多少“坑”是可以提前用参数填上的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值