SuperAPI AI 工具集

django-models

编程与开发

查看项目

更新于 10-16 10:59

技能介绍

用途

本技能用于 Django 项目的模型设计与查询优化,目标是按「厚模型、薄视图」的思路组织代码:业务逻辑归属模型与 Manager,视图只负责编排工作流。适用于设计模型、优化查询、封装领域逻辑等场景。

核心思想:厚模型、薄视图

业务逻辑放在模型和 Manager 中,而非视图里。视图负责编排流程,模型实现领域行为。

  • 好:模型方法处理业务规则、状态转换、校验。
  • 坏:视图中包含领域规则的 if/else 逻辑,或计算派生值。

模型设计

围绕领域概念组织模型:

  • 状态字段与枚举使用 TextChoices/IntegerChoices。
  • 添加 get_absolute_url() 提供规范对象 URL。
  • 包含 str() 以获得可读表示。
  • 在 Meta 中设置合适 ordering 以统一默认排序。
  • 为频繁过滤/排序的字段添加数据库索引。
  • 用抽象基类承载共享字段(时间戳、软删除等)。

字段选择指南

  • 可选文本字段用 blank=True, default=""(避免 null)。
  • 可选外键用 null=True, blank=True。
  • 唯一的可选字段用 null=True 以避免冲突问题。
  • 灵活元数据用 JSONField(避免创建大量可选字段)。
  • 根据实际数据需要设置合适的 max_length。

在模型方法中封装业务逻辑

  • 状态转换:post.publish()、order.cancel()。
  • 权限检查:post.is_editable_by(user)。
  • 复杂计算:invoice.calculate_total()。
  • 计算型只读值使用属性(property)。
  • 部分更新保存时指定 update_fields。

QuerySet 模式:组合的力量

自定义 QuerySet 类使查询可复用、可链式、可测试。

  • 模式:QuerySet 作为 Manager——定义带领域过滤方法的 QuerySet 子类,通过 objects = YourQuerySet.as_manager() 附加到模型,链式组合查询。
  • 收益:查询逻辑可在视图、任务、管理命令中复用;链式方法表达力强、可读;易于单独测试;将查询复杂度从视图中封装出去。
  • 常见 QuerySet 方法:按状态过滤;日期范围查询(近期、即将到来、已过期);按用户范围查询(owned_by、visible_to);组合查找(published_and_recent)。

查询优化:避免 N+1

  • select_related():用于 ForeignKey 和 OneToOneField(生成 SQL JOIN)。
  • prefetch_related():用于 ManyToManyField 和反向 ForeignKey(单独查询 + Python 拼接)。
  • only():不需要整个对象时只加载特定字段。
  • defer():排除不会用到的大字段(TextField、JSONField)。
  • Prefetch() 对象:用过滤器和 select_related 定制预取。

高效计数与存在性检查

  • 用 .exists() 替代 if queryset: 或 if len(queryset):。
  • 用 .count() 替代 len(queryset.all())。
  • 两者都在数据库层执行,不加载对象。

聚合与注解

  • annotate():为每个对象添加计算字段(Count、Sum、Avg 等)。
  • aggregate():在整个查询集上计算值。
  • 用 F() 表达式做数据库层更新(views=F('views') + 1)。
  • 将 annotate 与 filter 结合,表达「至少包含 N 个关联对象」。

Manager 与 QuerySet 的取舍

  • QuerySet 用于可链式的查询逻辑。
  • Manager 用于不返回 queryset 的模型级操作。
  • Manager:类似「工厂方法」,如 User.objects.create_user()。
  • QuerySet:类似「过滤器与变换」,如 Post.objects.published().recent()。
  • 多数情况下应自定义 QuerySet 而非自定义 Manager。

信号:谨慎使用

信号会造成隐式耦合,使代码更难追踪,优先显式方法调用。

  • 适合用信号:审计日志(跟踪模型全部变更)、缓存失效(模型变化时清缓存)、应用解耦(第三方应用需要响应你的模型)。
  • 应避免用信号:应放在模型方法中的业务逻辑;与调用方紧密耦合的逻辑(直接调用函数即可);复杂工作流(改用显式服务层)。
  • 经验法则:如果触发方和响应方都由你控制,就不要用信号。

迁移

  • 工作流:模型变更后运行 makemigrations;应用前审查生成的迁移文件;运行 migrate 应用;迁移尽可能可逆。
  • 数据迁移:用 makemigrations --empty app_name 创建;用 apps.get_model() 访问模型(不要直接导入);同时编写正向和反向操作。
  • 适用场景:填充新字段、转换数据、在字段间迁移。

应避免的反模式

查询反模式:

  • 遍历对象并访问关联关系时未使用 select_related()/prefetch_related()。
  • 用 if queryset: 替代 .exists()。
  • 用 len() 计数替代 .count()。
  • 只需要特定字段时却加载整个对象。 设计反模式:
  • 业务逻辑放在视图而非模型。
  • 视图执行本应属于模型方法的计算。
  • 同步操作中过度使用信号。
  • 能用 JSONField 时却创建新模型。
  • 忘记为过滤/排序字段添加索引。

集成

可与以下模式配合:

  • pytest-django-patterns:基于 Factory 的模型测试。
  • celery-patterns:模型上的异步操作(传 ID 而非实例)。
  • django-forms:ModelForm 校验与保存。