找回密码
 立即注册
首页 业界区 业界 Django Model高级特性

Django Model高级特性

师佳思 2025-10-28 18:35:00
1、字段参数深度解析

1.1  null vs blank


  • null:针对数据库层面,控制字段是否允许存储 NULL 值(默认 False)。  例:models.IntegerField(null=True) 表示数据库中该字段可以为 NULL。
  • blank:针对表单验证层面(如 Admin 后台或 ModelForm),控制字段是否允许提交空值(默认 False)。例:models.CharField(blank=True) 表示表单提交时该字段可以为空字符串(数据库存储为 "",而非 NULL)。
典型组合场景

  • 字符串类型(CharField、TextField):通常用 blank=True 而非 null=True(避免数据库中同时存在 "" 和 NULL 两种“空”,逻辑混乱)。
  • 数值/日期类型:若允许空,需 null=True(因为无法用空字符串表示数值空值)。
  1. class Example(models.Model):
  2.     name = models.CharField(max_length=50, blank=True)  # 表单可空,数据库存""
  3.     age = models.IntegerField(null=True, blank=True)  # 表单可空,数据库存NULL
复制代码
1.2 choices

用于将字段值限制在预设的选项中,数据库存储实际值,显示时用对应标签,适用于状态、类型等固定选项场景。
语法:字段 = models.类型(choices=[(实际值, 显示标签), ...], ...)
示例:定义“订单状态”字段
  1. class Order(models.Model):
  2.     # 定义选项(通常用类属性维护,便于复用)
  3.     STATUS_CHOICES = [
  4.         ("PENDING", "待支付"),
  5.         ("PAID", "已支付"),
  6.         ("SHIPPED", "已发货"),
  7.         ("COMPLETED", "已完成"),
  8.     ]
  9.     status = models.CharField(
  10.         max_length=20,
  11.         choices=STATUS_CHOICES,
  12.         default="PENDING"  # 默认值
  13.     )
  14. # 使用:获取显示标签
  15. order = Order.objects.get(id=1)
  16. print(order.status)  # 输出实际值:"PAID"
  17. print(order.get_status_display())  # 输出显示标签:"已支付"(Django自动生成的方法)
复制代码
1.3 unique 与 unique_together


  • unique=True:单个字段值在表中唯一(如用户名、邮箱)。
    1. email = models.EmailField(unique=True)  # 邮箱不可重复
    复制代码
  • unique_together:多个字段组合唯一(元数据配置,适用于“联合唯一”场景,如“同一用户在同一日期只能创建一条记录”)。
    1. class UserRecord(models.Model):
    2.     user = models.ForeignKey(User, on_delete=models.CASCADE)
    3.     record_date = models.DateField()
    4.     content = models.TextField()
    5.     class Meta:
    6.         # 联合唯一:user + record_date 组合不可重复
    7.         unique_together = ["user", "record_date"]
    复制代码
    注意:Django 1.11+ 推荐用 UniqueConstraint(更灵活,支持条件)替代 unique_together,在 Meta.constraints 中定义:
    1. class Meta:
    2. constraints = [
    3.      models.UniqueConstraint(
    4.          fields=["user", "record_date"],
    5.          name="unique_user_record_date"  # 约束名
    6.      )
    7. ]
    复制代码

1.4 db_index

为高频查询字段添加索引,提升查询效率(索引会减慢写入速度,需合理使用)。
  1. class Article(models.Model):
  2.     title = models.CharField(max_length=100, db_index=True)  # 对标题建索引,加速按标题查询
  3.     publish_time = models.DateTimeField(db_index=True)  # 对发布时间建索引,加速按时间排序/筛选
复制代码
1.5 validators

通过验证器函数对字段值进行自定义校验(如手机号格式、密码强度)。
示例:验证手机号为11位数字
  1. from django.core.validators import RegexValidator
  2. class UserProfile(models.Model):
  3.     # 正则验证:11位数字
  4.     phone = models.CharField(
  5.         max_length=11,
  6.         validators=[
  7.             RegexValidator(
  8.                 regex=r'^1[3-9]\d{9}$',
  9.                 message="手机号必须是11位数字,且以13-19开头"
  10.             )
  11.         ]
  12.     )
复制代码
验证触发时机:调用 model.full_clean() 时(如表单提交、手动验证)。
2、元数据(Meta)高级配置

class Meta 用于定义模型的元数据(非字段数据),控制表行为、排序、索引等,常用配置如下:
配置项作用示例db_table指定数据库表名(默认:app_label_模型名)db_table = "my_book"ordering默认排序规则(查询时自动应用)ordering = ["-publish_time", "title"](按发布时间降序,再按标题升序)indexes定义复合索引(多字段联合索引)indexes = [models.Index(fields=["author", "price"])]abstract标记为抽象基类(不生成表,仅用于继承)abstract = Trueverbose_name模型单数显示名(Admin后台用)verbose_name = "文章"verbose_name_plural模型复数显示名(中文通常与单数相同)verbose_name_plural = "文章"default_related_name反向关联的默认名称(替代 related_name)若模型被多个外键关联,避免 related_name 冲突managed是否由Django迁移管理表(默认True)managed = False(用于映射已有表,不自动创建/删除)示例:完整的Meta配置
  1. class Article(models.Model):
  2.     title = models.CharField(max_length=100)
  3.     author = models.ForeignKey(User, on_delete=models.CASCADE)
  4.     publish_time = models.DateTimeField(auto_now_add=True)
  5.     read_count = models.IntegerField(default=0)
  6.     class Meta:
  7.         db_table = "article"  # 表名
  8.         ordering = ["-publish_time"]  # 默认按发布时间降序
  9.         verbose_name = "文章"
  10.         verbose_name_plural = "文章"
  11.         indexes = [
  12.             # 对author和read_count建复合索引,加速“查询某作者的高阅读量文章”
  13.             models.Index(fields=["author", "read_count"]),
  14.         ]
  15.         # 联合唯一:同一作者不能有相同标题的文章
  16.         constraints = [
  17.             models.UniqueConstraint(fields=["author", "title"], name="unique_author_title")
  18.         ]
复制代码
3、查询集(QuerySet)高级操作与优化

Django 的 QuerySet 是查询结果的集合,支持链式调用和延迟加载(Lazy Loading),但不合理使用会导致性能问题(如 N+1 查询)。以下是关键优化技巧:
3.1 延迟加载与立即执行


  • 延迟加载:QuerySet 创建时不执行 SQL,直到迭代、切片、序列化等操作时才执行(减少不必要的数据库请求)。
    1. qs = Book.objects.filter(author="张三")  # 未执行SQL
    2. for book in qs:  # 迭代时执行SQL
    3.     print(book.title)
    复制代码
  • 立即执行:通过 list() 或 len() 强制执行 SQL(谨慎使用,避免提前加载大量数据)。
3.2  select_related

优化一对一/外键(多对一)查询,正向查询
解决“查询主表时,关联表多次查询”的问题(N+1 问题),通过 SQL JOIN 一次性加载关联数据。
场景:查询书籍时,同时获取其关联的作者信息(外键关系)。
  1. # 未优化:查询10本书,会执行1次Book查询 + 10次Author查询(N+1)
  2. books = Book.objects.all()
  3. for book in books:
  4.     print(book.author.name)  # 每次访问author都会触发新查询
  5. # 优化:用select_related一次性加载关联的Author
  6. books = Book.objects.select_related("author").all()  # 仅1次JOIN查询
  7. for book in books:
  8.     print(book.author.name)  # 直接从内存获取,无额外查询
复制代码
3.3 prefetch_related

优化多对多/反向外键查询
针对多对多或反向一对多关系(无法用 JOIN 优化),通过 Python 层面合并查询结果,减少 SQL 次数。
场景:查询作者时,同时获取其所有书籍(反向外键,author.books.all())。
  1. # 未优化:查询10个作者,会执行1次Author查询 + 10次Book查询(N+1)
  2. authors = Author.objects.all()
  3. for author in authors:
  4.     print(author.books.all())  # 每次访问books触发新查询
  5. # 优化:用prefetch_related预加载关联的books
  6. authors = Author.objects.prefetch_related("books").all()  # 2次查询(Author + Book)
  7. for author in authors:
  8.     print(author.books.all())  # 从内存获取,无额外查询
复制代码
高级用法:Prefetch 对象自定义预加载查询(如筛选关联数据)
  1. from django.db.models import Prefetch
  2. # 预加载作者的“已出版”书籍(过滤条件)
  3. authors = Author.objects.prefetch_related(
  4.     Prefetch(
  5.         "books",
  6.         queryset=Book.objects.filter(is_published=True),  # 筛选条件
  7.         to_attr="published_books"  # 自定义属性名(替代默认的books)
  8.     )
  9. ).all()
  10. for author in authors:
  11.     print(author.published_books)  # 仅包含已出版的书籍
复制代码
3.4 只查询需要的字段:only 与 defer


  • only(*fields):仅加载指定字段(其他字段访问时会触发新查询)。
  • defer(*fields):延迟加载指定字段(仅在访问时查询)。
适用于只需部分字段的场景,减少数据传输量。
  1. # 仅加载title和price字段(高效)
  2. books = Book.objects.only("title", "price")
  3. for book in books:
  4.     print(book.title, book.price)  # 直接获取
  5.     # print(book.author)  # 访问未指定字段,触发新查询
  6. # 延迟加载description(大文本字段,默认不加载)
  7. books = Book.objects.defer("description")
  8. for book in books:
  9.     print(book.title)  # 直接获取
  10.     # print(book.description)  # 访问时才查询
复制代码
3.5 聚合与分组查询(aggregate 与 annotate)


  • aggregate:对查询集进行聚合计算(如求和、平均值),返回字典结果。
  • annotate:对每个对象添加聚合计算结果(分组聚合)。
示例
  1. from django.db.models import Sum, Avg, Count
  2. # 1. 聚合:计算所有书籍的平均价格、总数量
  3. result = Book.objects.aggregate(
  4.     avg_price=Avg("price"),
  5.     total=Count("id")
  6. )
  7. print(result)  # {'avg_price': 59.99, 'total': 100}
  8. # 2. 分组聚合:计算每个作者的书籍数量
  9. authors = Author.objects.annotate(
  10.     book_count=Count("books")  # 按作者分组,统计其books数量
  11. )
  12. for author in authors:
  13.     print(f"{author.name} 有 {author.book_count} 本书")
复制代码
4、模型继承

Django 支持三种模型继承方式,用于代码复用和逻辑抽象:
4.1 抽象基类(Abstract Base Classes)


  • 定义一个基类,标记为 abstract=True,自身不生成数据库表,仅用于被子类继承。
  • 子类会包含基类的所有字段。
示例:定义“时间戳”抽象基类(自动记录创建和更新时间)
  1. class TimeStampedModel(models.Model):
  2.     created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
  3.     updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
  4.     class Meta:
  5.         abstract = True  # 标记为抽象基类
  6. # 子类继承:自动拥有created_at和updated_at字段
  7. class Article(TimeStampedModel):
  8.     title = models.CharField(max_length=100)
  9.     content = models.TextField()
  10. class Comment(TimeStampedModel):
  11.     text = models.CharField(max_length=200)
  12.     article = models.ForeignKey(Article, on_delete=models.CASCADE)
复制代码
4.2 多表继承(Multi-table Inheritance)


  • 父类和子类各自生成数据库表,子类通过隐式的一对一外键关联父类。
  • 适用于“子类是父类的特殊类型”场景(如“用户”和“超级用户”)。
示例
  1. class User(models.Model):
  2.     name = models.CharField(max_length=50)
  3.     email = models.EmailField()
  4. # 多表继承:SuperUser是User的子类,会生成superuser表,包含user_ptr外键关联user表
  5. class SuperUser(User):
  6.     is_admin = models.BooleanField(default=True)
  7.     permissions = models.TextField()
  8. # 使用:
  9. su = SuperUser.objects.create(name="admin", email="admin@example.com", permissions="all")
  10. print(su.name)  # 访问父类字段
  11. print(su.is_admin)  # 访问子类字段
复制代码
4.3 代理模型(Proxy Models)


  • 不创建新表,仅为原模型创建“代理”,用于修改模型的行为(如默认排序、新增方法),不添加新字段。
  • 适用于“同一数据,不同操作逻辑”场景(如“普通用户看到的订单”和“管理员看到的订单”)。
示例:为 Order 模型创建代理,修改默认排序和新增方法
  1. class Order(models.Model):
  2.     status = models.CharField(max_length=20)
  3.     create_time = models.DateTimeField(auto_now_add=True)
  4. # 代理模型:不改变表结构,仅修改行为
  5. class RecentOrder(Order):
  6.     class Meta:
  7.         proxy = True  # 标记为代理模型
  8.         ordering = ["-create_time"]  # 默认按创建时间降序(仅代理生效)
  9.     # 新增方法:筛选最近7天的订单
  10.     @classmethod
  11.     def recent_7days(cls):
  12.         from django.utils import timezone
  13.         seven_days_ago = timezone.now() - timezone.timedelta(days=7)
  14.         return cls.objects.filter(create_time__gte=seven_days_ago)
  15. # 使用:操作代理模型等同于操作原模型,但有自定义行为
  16. recent_orders = RecentOrder.recent_7days()  # 调用自定义方法
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册