django-models

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Django Model Patterns

Django 模型模式

Core Philosophy: Fat Models, Thin Views

核心理念:胖模型、瘦视图

Business logic belongs in models and managers, not views. Views orchestrate workflows; models implement domain behavior. This principle creates testable, reusable code that stays maintainable as complexity grows.
Good: Model methods handle business rules, state transitions, validation Bad: Views contain if/else logic for domain rules, calculate derived values
业务逻辑应放在 models 和 managers 中,而不是 views 中。 views 负责编排工作流;models 实现领域行为。这一原则能够创建可测试、可复用的代码,并在复杂度不断增长时保持可维护性。
良好做法:由 model 方法处理业务规则、状态转换和验证 不良做法:views 中包含针对领域规则的 if/else 逻辑,并计算派生值

Model Design

模型设计

Structure Your Models Around Domain Concepts

围绕领域概念组织模型

  • Use
    TextChoices
    /
    IntegerChoices
    for status fields and enums
  • Add
    get_absolute_url()
    for canonical object URLs
  • Include
    __str__()
    for readable representations
  • Set proper
    ordering
    in Meta for consistent default sorting
  • Add database indexes for frequently filtered/sorted fields
  • Use abstract base models for shared fields (timestamps, soft deletes, etc.)
  • 对状态字段和枚举使用
    TextChoices
    /
    IntegerChoices
  • 添加
    get_absolute_url()
    ,提供对象的规范 URL
  • 包含
    __str__()
    ,以提供易读的表示形式
  • 在 Meta 中设置适当的
    ordering
    ,确保一致的默认排序
  • 为经常用于筛选或排序的字段添加数据库索引
  • 使用抽象基模型复用共享字段(时间戳、软删除等)

Field Selection Guidelines

字段选择指南

  • Use
    blank=True, default=""
    for optional text fields (avoid null)
  • Use
    null=True, blank=True
    for optional foreign keys
  • For unique optional fields, use
    null=True
    to avoid collision issues
  • Leverage
    JSONField
    for flexible metadata (avoid creating many optional fields)
  • Set appropriate
    max_length
    based on actual data needs
  • 对可选文本字段使用
    blank=True, default=""
    (避免使用 null)
  • 对可选外键使用
    null=True, blank=True
  • 对唯一可选字段使用
    null=True
    ,以避免冲突问题
  • 使用
    JSONField
    存储灵活的元数据(避免创建大量可选字段)
  • 根据实际数据需求设置适当的
    max_length

Encapsulate Business Logic in Model Methods

在 model 方法中封装业务逻辑

  • State transitions:
    post.publish()
    ,
    order.cancel()
  • Permission checks:
    post.is_editable_by(user)
  • Complex calculations:
    invoice.calculate_total()
  • Use properties for computed read-only values
  • Specify
    update_fields
    when saving partial changes
  • 状态转换:
    post.publish()
    、
    order.cancel()
  • 权限检查:
    post.is_editable_by(user)
  • 复杂计算:
    invoice.calculate_total()
  • 使用 property 表示只读的计算值
  • 保存部分更改时指定
    update_fields

QuerySet Patterns: The Power of Composition

QuerySet 模式:组合的力量

Custom QuerySet classes are your secret weapon. They make queries reusable, chainable, and testable.
自定义 QuerySet 类是你的秘密武器。 它们能够让查询变得可复用、可链式调用且易于测试。

Pattern: QuerySet as Manager

模式:将 QuerySet 作为 Manager

Define a QuerySet subclass with domain-specific filter methods
Attach it to your model: objects = YourQuerySet.as_manager()
Chain methods for composable queries
定义一个包含领域特定筛选方法的 QuerySet 子类
将其附加到 model:objects = YourQuerySet.as_manager()
链式调用方法以组合查询

Benefits

优势

  • Reusable query logic across views, tasks, management commands
  • Chainable methods enable expressive, readable queries
  • Easy to test in isolation
  • Encapsulates query complexity away from views
  • 可在 views、tasks 和 management commands 中复用查询逻辑
  • 链式方法支持表达性强、易读的查询
  • 易于独立测试
  • 将查询复杂度与 views 隔离

Common QuerySet Methods

常用的 QuerySet 方法

  • Filtering by status/state
  • Date range queries (recent, upcoming, expired)
  • User-scoped queries (owned_by, visible_to)
  • Combined lookups (published_and_recent)
  • 按 status/state 筛选
  • 日期范围查询(recent、upcoming、expired)
  • 按用户限定查询(owned_by、visible_to)
  • 组合查询条件(published_and_recent)

Query Optimization: Avoid N+1 Queries

查询优化:避免 N+1 查询

The Golden Rules

黄金规则

  1. select_related(): Use for ForeignKey and OneToOneField (creates SQL JOIN)
  2. prefetch_related(): Use for ManyToManyField and reverse ForeignKeys (separate query + Python join)
  3. only(): Load specific fields when you don't need the whole object
  4. defer(): Exclude heavy fields (TextField, JSONField) you won't use
  5. Prefetch() object: Customize prefetch with filters and select_related
  1. select_related()
    :用于 ForeignKey 和 OneToOneField(创建 SQL JOIN)
  2. prefetch_related()
    :用于 ManyToManyField 和反向 ForeignKey(执行单独的查询并在 Python 中 join)
  3. only()
    :当不需要整个 object 时,仅加载特定字段
  4. defer()
    :排除不会使用的较重字段(TextField、JSONField)
  5. Prefetch()
    对象
    :使用 filters 和 select_related 自定义预取行为

Efficient Counting and Existence Checks

高效计数与存在性检查

  • Use
    .exists()
    instead of
    if queryset:
    or
    if len(queryset):
  • Use
    .count()
    instead of
    len(queryset.all())
  • Both perform database-level operations without loading objects
  • 使用
    .exists()
    ,而不是
    if queryset:
    或
    if len(queryset):
  • 使用
    .count()
    ,而不是
    len(queryset.all())
  • 两者都会在数据库级别执行操作,而不会加载 objects

Aggregation and Annotation

聚合与注解

  • annotate()
    : Add computed fields to each object (Count, Sum, Avg, etc.)
  • aggregate()
    : Compute values across entire queryset
  • Use
    F()
    expressions for database-level updates (
    views=F('views') + 1
    )
  • Combine annotate with filter for "objects with at least N related items"
  • annotate()
    :为每个 object 添加计算字段(Count、Sum、Avg 等)
  • aggregate()
    :计算整个 queryset 的值
  • 使用
    F()
    表达式执行数据库级更新(
    views=F('views') + 1
    )
  • 将 annotate 与 filter 结合,用于查询“至少包含 N 个关联项的 objects”

Managers vs QuerySets

Managers 与 QuerySets

Use QuerySets for chainable query logic. Use Managers for model-level operations that don't return querysets.
Manager: Think "factory methods" -
User.objects.create_user()
QuerySet: Think "filters and transformations" -
Post.objects.published().recent()
Most of the time, you want a custom QuerySet, not a custom Manager.
使用 QuerySets 实现可链式调用的查询逻辑。 使用 Managers 执行不返回 querysets 的 model 级操作。
Manager:可理解为“工厂方法”——
User.objects.create_user()
QuerySet:可理解为“筛选和转换”——
Post.objects.published().recent()
大多数时候,你需要的是自定义 QuerySet,而不是自定义 Manager。

Signals: Use Sparingly

Signals:谨慎使用

Signals create implicit coupling and make code harder to follow. Prefer explicit method calls.
Signals 会产生隐式耦合,使代码更难理解。优先使用显式的方法调用。

When Signals Make Sense

适合使用 Signals 的场景

  • Audit logging (track all changes to a model)
  • Cache invalidation (clear cache when model changes)
  • Decoupling apps (third-party app needs to react to your models)
  • 审计日志(跟踪 model 的所有更改)
  • 缓存失效(model 发生变化时清除缓存)
  • 解耦 apps(第三方 app 需要对你的 models 作出响应)

When to Avoid Signals

应避免使用 Signals 的场景

  • Business logic that should be in model methods
  • Logic tightly coupled to the calling code (just call the function directly)
  • Complex workflows (use explicit service layer instead)
Rule of thumb: If you control both the trigger and the reaction, don't use a signal.
  • 本应放在 model 方法中的业务逻辑
  • 与调用代码紧密耦合的逻辑(直接调用函数即可)
  • 复杂的工作流(改用显式 service layer)
经验法则:如果你同时控制触发操作和响应操作,就不要使用 signal。

Migrations

Migrations

Workflow

工作流

  • Run
    makemigrations
    after model changes
  • Review generated migration files before applying
  • Run
    migrate
    to apply migrations
  • Migrations should be reversible when possible
  • model 更改后运行
    makemigrations
  • 应用前检查生成的 migration 文件
  • 运行
    migrate
    应用 migrations
  • 尽可能让 migrations 可逆

Data Migrations

数据 Migrations

Create with
makemigrations --empty app_name
. Use
apps.get_model()
to access models (not direct imports). Write both forward and reverse operations.
Use data migrations for: Populating new fields, transforming data, migrating between fields.
使用
makemigrations --empty app_name
创建。使用
apps.get_model()
访问 models(不要直接导入)。同时编写正向和反向操作。
数据 migrations 的适用场景:填充新字段、转换数据、在字段之间迁移数据。

Anti-Patterns to Avoid

应避免的反模式

Query Anti-Patterns

查询反模式

  • Iterating over objects and accessing relations without
    select_related()
    /
    prefetch_related()
  • Using
    if queryset:
    instead of
    .exists()
  • Using
    len()
    to count instead of
    .count()
  • Loading entire objects when you only need specific fields
  • 遍历 objects 并访问 relations,却未使用
    select_related()
    /
    prefetch_related()
  • 使用
    if queryset:
    ,而不是
    .exists()
  • 使用
    len()
    计数,而不是
    .count()
  • 只需要特定字段时,却加载整个 objects

Design Anti-Patterns

设计反模式

  • Business logic in views instead of models
  • Views performing calculations that belong in model methods
  • Overusing signals for synchronous operations
  • Creating new models when JSONField would suffice
  • Forgetting to add indexes for filtered/sorted fields
  • 将业务逻辑放在 views 中,而不是 models 中
  • 在 views 中执行本应由 model 方法完成的计算
  • 为同步操作滥用 signals
  • 在 JSONField 足以满足需求时创建新 models
  • 忘记为经过筛选或排序的字段添加索引

Integration

集成

Works with:
  • pytest-django-patterns: Factory-based model testing
  • celery-patterns: Async operations on models (pass IDs, not instances)
  • django-forms: ModelForm validation and saving
兼容:
  • pytest-django-patterns:基于 factory 的 model 测试
  • celery-patterns:针对 models 的异步操作(传递 ID,而不是 instances)
  • django-forms:ModelForm 验证和保存