关于 ac-django
ac-django 是面向 Django 项目开发者的技能,也提供可选的 DRF 指南。它按版本整理建议,并以专题参考资料覆盖模型、迁移、视图、测试和基础设施;规则建立在 ac-python 之上。
主要功能
- 01Django 6.1 指南与 Django 5.2 LTS 说明
- 02可选的 DRF 实践指南
- 03按开发主题组织的参考资料
- 04模型、迁移与查询设计建议
- 05Django 专用测试和工具约定
适用场景
- 01设计模型和数据库迁移
- 02审查 DRF 序列化器与查询性能
- 03实现视图、表单或后台任务
- 04编写 Django 测试并配置工具
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ souliane/ skills --skill ac-django安装后在 Claude Code 中输入/ac-django确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/admin-and-drf.mdantipatterns.mdbackground-and-infra.mdmodels-and-schema.mdsquashing-migrations.mdtesting-and-tooling.mdtransactions-and-migrations.mdtroubleshooting.mdviews-and-templates.mdrules/astgrep_scan.pyno-complexity-suppressions.ymlno-pytest-django-db.ymlpyproject_complexity.pytestcase-no-pytest-parametrize.yml详细介绍
Django 指南(Django 6 · DRF 可选)
目标版本: Python 3.14 上的 Django 6.1 · 上一版本线: Django 5.2 LTS(末尾章节)· API: 按需选择 DRF
版本策略
本技能只记录一个 Django 版本线:当前版本线。更新时请保持这一原则。
- 主线 = 当前功能版本。 目前是 Django 6.1。有更新的功能版本发布时,主线应在同一次编辑中切换到新版本。
- 未标注的正文表示当前版本线的首个版本——Django 6.0。该版本线在此之后新增的内容,都应在其对应的 API 或功能之后紧跟
(6.1+)标记:`QuerySet.fetch_mode()` (6.1+)。只采用这一种形式。不要写“Django X.Y+ 新增”“X.Y 新增”或“自 X.Y 起”,也不要使用### Django X.Y 注释标题——标记就是全部约定,而且只有保持唯一时才有用。 - 旧版本线各自在
SKILL.md末尾保留一个章节,其他任何地方都不应提及——参考文件仅涵盖当前版本线,末尾也不设自己的章节。目前该章节为## Previous line: Django 5.2 LTS。其中记录该版本的读者需要了解的差异:缺少什么、可用什么替代,以及升级时需要做什么。若版本说明打断了主线内容,就是缺陷——将其移到末尾。 - 更新时的调研顺序。 首先查阅发行说明和官方文档(
docs.djangoproject.com/en/<version>/releases/<version>/及其链接的主题页面)。然后查阅“权威来源”中已经引用的作者。最后简要查阅博客和时事通讯,了解文档未涵盖的内容。 - 轮换规则。 Django 发布下一条版本线时,当前的末尾章节替换为即将退役的版本线;未标注的正文改以新版本线的首个版本为基准,并移除低于该版本的标记。Django 6.2 是
A.B版本方案下的最后一个版本;之后的功能版本将采用日历年份YYYY格式,每年 1 月发布一次。届时,“当前版本线”指最新的YYYY版本,其他规则保持不变。
权威来源
- Django 文档索引:https://docs.djangoproject.com/en/6.1/
- Django 6.1 发行说明:https://docs.djangoproject.com/en/6.1/releases/6.1/
- Django 6.0 发行说明——本技能中未标注内容所依据的版本:https://docs.djangoproject.com/en/6.0/releases/6.0/
- Django 文档——获取模式:https://docs.djangoproject.com/en/6.1/topics/db/fetch-modes/
- Django 文档——将 email 迁移到 mailers:https://docs.djangoproject.com/en/6.1/topics/email/#migrating-email-to-mailers
- Django 文档——压缩迁移:https://docs.djangoproject.com/en/6.1/topics/migrations/#migration-squashing
- Adam Johnson——
django-upgrade:https://adamj.eu/tech/2021/09/16/introducing-django-upgrade/ - Adam Johnson——
django-linear-migrations:https://adamj.eu/tech/2020/12/10/introducing-django-linear-migrations/ - Adam Johnson——测试是否有待处理的迁移:https://adamj.eu/tech/2024/06/23/django-test-pending-migrations/
- Adam Johnson——无需迁移即可更改的模型字段选项:https://adamj.eu/tech/2025/05/03/django-choices-change-without-migration/
- Haki Benita——“如何把外键搞得一团糟”:https://hakibenita.com/django-foreign-keys
- Haki Benita——可靠的 Django Signals(django-tasks-db 生产模式):https://hakibenita.com/django-reliable-signals
- James Bennett——胖模型/“不使用服务层”(默认做法)及关于拆分上帝方法的后续文章:https://www.b-list.org/weblog/2020/mar/16/no-service/ · https://www.b-list.org/weblog/2020/mar/23/still-no-service/
- James Bennett——“Litestar 值得一看”(重申 Django 特别不需要服务层,同时允许在约束较少的框架中使用服务层):https://www.b-list.org/weblog/2025/aug/06/litestar/
- DabApps——模型封装(切勿从外部写入字段或调用
save()):https://www.dabapps.com/insights/django-models-and-encapsulation/ - HackSoft Django Styleguide——服务层/
selectors.py阵营(这是一种选择,并非本技能的默认做法):https://github.com/HackSoftware/Django-Styleguide - DRF 文档:https://www.django-rest-framework.org/
- Factory Boy 最佳实践:https://github.com/camilamaia/factory-boy-best-practices/
参考文件(按需加载)
| 文件 | 涵盖内容 | 加载时机 |
|---|---|---|
references/models-and-schema.md | 模型、字段、约束、QuerySet、管理器、ORM 性能 | 模型变更、查询优化、架构设计 |
references/transactions-and-migrations.md | 事务、锁定、幂等性、迁移安全、外键索引操作 | 多步写入、迁移、架构变更 |
references/squashing-migrations.md | 压缩应用的迁移:重新生成并保留 replaces(默认方式)、使用 squashmigrations,或在不使用 replaces 的情况下重写 django_migrations;拒绝处于迁移中途的数据库;回滚 | 缩短已部署数据库运行过的冗长迁移历史 |
references/views-and-templates.md | 视图、表单、模板、局部模板、HTMX、文件上传、国际化、中间件、管理命令、连接池、参考代码片段 | 视图/模板开发、表单处理、HTMX、上传 |
references/background-and-infra.md | 后台任务、安全、设置、可观测性、缓存、异步 | 任务、部署、日志记录、缓存、异步视图 |
references/admin-and-drf.md | Django Admin、DRF 序列化器、视图集、权限、分页、版本控制 | Admin 自定义、API 端点 |
references/testing-and-tooling.md | 测试指南、Factory Boy、工具、开发体验规范 | 编写测试、CI 设置、代码检查 |
references/antipatterns.md | django-antipatterns.com 提示与模式 | 代码审查、避免常见错误 |
references/troubleshooting.md | 常见 Django 错误及修复方法 | 诊断迁移、N+1、on_commit 问题 |
依赖项
需要 ac-python(通过 requires: frontmatter 字段声明)。此技能在 ac-python 的通用 Python 指南之上叠加 Django 专属规则——请参阅下文“与 ac-python 一起加载时的覆盖规则”。
与生命周期技能搭配使用时,为采用 worktree 工作流的 Django 项目提供 Django 最佳实践背景。
与 ac-python 一起加载时的覆盖规则
同时加载 ac-django 和 ac-python 时,以下 Django 专属规则优先于通用 Python 指南:
| 主题 | ac-python(通用) | ac-django(优先) |
|---|---|---|
| 测试基类 | 普通 pytest 类(class TestFoo:) | django.test.TestCase(必要时使用 TransactionTestCase) |
| 参数化 | pytest.mark.parametrize | unittest_parametrize |
| 测试数据工厂 | 普通 build_...() 函数 | 使用 DjangoModelFactory、traits、build()/create() 的 Factory Boy |
| 共享设置 | pytest fixtures | setUpTestData()(类级别,对数据库测试更快) |
| 时间模拟 | 任意工具(freezegun、time_machine 等) | time_machine(团队默认) |
ac-python 的其他所有指南(风格、类型标注、OOP、导入、ruff 配置)在 Django 项目中均保持不变。
触发 QA(发布门槛)
发布技能变更前,使用示例提示词验证激活行为:
- 应触发:
- “添加一个 Django 模型字段和迁移。”
- “检查这个 DRF 序列化器和 QuerySet 的性能问题。”
- “修复这个 Django 事务/on_commit 错误。”
- 不应触发:
- “为一个工单设置 git worktree。”
- “实施项目交付工作流并创建 MR。”
- “创建一份 Notion 调研摘要。”
如果触发不足或过度触发,请在发布前收紧 description 中的提示线索。
示例:添加新的模型字段
用户说:“在 Address 模型中添加一个 postal_code 字段”
- 加载
references/models-and-schema.md,了解字段类型和约束 - 加载
references/transactions-and-migrations.md,了解迁移安全性 - 向模型添加字段,并设置适当的验证器、约束;如果会用于查询,则设置
db_index - 创建迁移,并使用
makemigrations --check验证 - 在测试中为新字段添加工厂特征
首要原则
始终优先查阅 Django 文档
- 按照 Django 文档说明的方式使用 Django。
- 优先使用内置功能,除非 Django 存在明确的文档说明的不足。
默认采用胖模型(但并非唯一答案)
对于中小型模型,胖模型是首选默认方案,而非教条。
- 业务规则和不变量的默认归属位置:
- 模型实例方法(单个对象的行为——Django 所说的“行级”归属位置)
- QuerySet/Manager 方法(集合行为——Django 所说的“表级”归属位置)
- 这符合 Django 文档以及 James Bennett 的“无服务层”主张:模型及其管理器和查询集_本身就是_其他代码所交互的 API。
- 默认情况下,领域逻辑不放在
services.py中。
当模型变成上帝对象时,胖模型就不再合适。胖模型是指围绕_自身_数据拥有丰富且内聚行为的模型;上帝对象则是指一个模型不断累积互不相关的职责和编排逻辑,最终变得无人能从头到尾读懂。有关相关信号和升级路径,请参阅下文“胖模型何时变成上帝对象”——应对方式_不是_条件反射般地引入服务层。
行为就近放置(避免章鱼式扩散)
- 将行为与以下内容放在一起:
- 数据(模型/查询集)
- UI(放在使用它们的模板中的局部模板)
协调与业务逻辑
可在边界层(视图/表单/序列化器)执行:
- 安排调用顺序
- 选择聚合根对象
- 跨多个领域调用时包裹事务
不可在边界层执行:
- 不变量
- 工作流规则
- 定义正确性的状态转换
弃用提示(在 6.1 中标记,将在下一个主版本中移除)
避免在新代码中使用以下内容:
- 扁平的
EMAIL_*设置(EMAIL_BACKEND、EMAIL_HOST、EMAIL_PORT等)已弃用,建议改用MAILERS字典设置(与DATABASES/CACHES类似);新代码不应再添加扁平设置。 mail.get_connection(),以及邮件辅助函数的connection/fail_silently/auth_user/auth_password参数——应改为传入using=和一个MAILERS别名。- 不带参数调用的
QuerySet.select_related(),以及ModelAdmin.list_select_related = True——始终传入明确的字段名称。 - 未指定明确字段名的
QuerySet.values_list(flat=True)。 - 未接受
fetch_mode关键字参数的Model.from_db()实现。 - 在
JSONField的精确查找右侧使用None来表示 JSON 标量null——请改用JSONNull()。仅影响查询:存储None仍会写入 SQLNULL,不受影响,键查找和索引查找也不受影响。 - 双点模板查找(
{{ book..title }}),它会解析为对空字符串的查找。 django.db.transaction.savepoint()——请改用savepoint_create()。salted_hmac()/base64_hmac()的默认 HMAC 算法将从sha1切换为sha256——不要将对sha1的预期硬编码。
Django 6.1 的说明将 Django 7.0 列为移除版本。Django 发布页面指出,6.2 是采用 A.B 版本方案的最后一个版本,此后的功能版本将采用 YYYY 格式。上游没有明确说明“7.0”如何对应这一版本方案。
项目布局与边界
领域优先的应用
- 按领域能力拆分应用,而不是按技术层拆分。
- 每个应用都应能从头到尾读懂。
公共、内部与管理界面
- 将公共 API/HTML 界面与内部/管理界面分开。
- 优先使用多个独立模块,而不是一个庞大的
views.py。
“core/” 准则
- 允许: 请求 ID 中间件、日志辅助工具、设置检查、小型共享基类混入
- 禁止: 仅仅因为某些行为是共享的,就隐藏领域行为
命名
- 使用业务语言。
- 优先使用动词方法:
approve()、reject()、settle()、cancel()。
团队风格约定(询问一次并记住)
有些风格选择同样合理——哪种“正确”取决于团队。在项目中首次遇到以下任一情况时,询问用户团队的偏好,并将其保存到项目的 MEMORY.md(自动记忆)中,以便在后续会话中沿用。
| 主题 | 选项 A | 选项 B | 询问内容 |
|---|---|---|---|
| 属性/关联检查 | LBYL: hasattr()、isinstance()、getattr(obj, attr, default) | EAFP: try/except AttributeError | “团队在检查可选属性和反向关联时,更倾向于 LBYL(hasattr/getattr),还是 EAFP(try/except)?” |
| QuerySet 是否存在的检查 | if qs.exists(): 然后 qs.first() | obj = qs.first(); if obj: | “使用单次查询(first() + None 检查),还是显式调用 exists() + first()?” |
| 序列化器中的空值处理 | 对每个可空字段显式设置 allow_null=True | 依赖模型字段的 null=True 推断 | “在序列化器字段上显式设置 allow_null,还是从模型推断?” |
规则:
- 每个项目只在首次出现时询问——如果已保存,就不要再次询问。
- 如果项目覆盖技能已记录该偏好,将其视为答案——不要再次询问。
- 将答案保存为
project类型的记忆,并使用描述性标题(例如:“Django 团队约定:属性检查采用 LBYL”)。
胖模型原则(逻辑所在之处)
放置表(严格)
| 关注点 | 所属位置 | 备注 |
|---|---|---|
| 不变量、状态转换、领域计算 | 模型方法 | “告知,不询问” |
| 集合逻辑 | QuerySet 方法 | 可链式调用 |
| 图加载 | QuerySet 方法 | for_api()、for_list()、with_*() |
| 请求验证 | 表单/DRF 序列化器 | 边界验证 |
| 授权 | 视图/DRF 权限 | 尽早检查 |
| 渲染 | 模板/序列化器 | 不访问数据库 |
封装变更
公开领域方法,按以下顺序执行:验证状态 → 执行变更 → 持久化更改 → 安全地安排副作用(在提交后)。
让胖模型保持名副其实的封装规则(DabApps):绝不在模型外直接写入模型字段,也不调用 save()/create()/批量操作——应通过模型方法或管理器方法执行。视图/模板代码可以_读取_任意字段,但状态变更始终必须通过上述方法之一完成。这正是“胖模型”的含义:模型负责维护自身的不变量,而不是“模型中有很多代码”。
当臃肿模型变成上帝对象
臃肿模型适用于小型和中型模型。但对于一个已经吞并了无关职责的模型,这条规则就不适用了。将以下情况视为上帝对象的信号——当同一个模型出现两种或更多信号时,就不要再往里添加职责,而应按照下面的升级路径进行拆分:
- **规模:**模型类主体超过约 200 行代码,或公开方法超过约 15–20 个。
- **领域混杂:**同一个模型中的方法涉及不属于该模型自身数据的职责(例如,
Order既调用计费服务提供商,又生成配送标签,还计算忠诚度积分)。 - 在
save()中编排流程:save()(或某个单一方法)驱动跨多个其他模型的多步骤工作流、发送通知或调用外部 API——例如“取消计费协议时,一个方法会更新其他六七项内容”的情况。 - **按职责划分的方法数量:**明显属于不同职责的方法群(例如,一个
User同时负责身份验证、个人资料、计费和功能标记)。 - **测试发出警报:**对某个方法进行单元测试时,必须设置半个数据库架构;或者某个模型对应的单个测试模块变得难以管理。
如果一个较长的模型仍然只涉及自身数据,且结构清晰,那它就不是上帝对象——不要仅仅因为代码行数多就重构。触发重构的是职责混杂或流程编排,而不是单纯的规模。
升级阶梯(只在信号迫使时向上攀升)
每一级都有一个 适用时机。选择能解决信号的最低一级——不要跳到服务层。
-
(a) QuerySet / Manager 方法。 适用时机: 逻辑属于集合级或表级(筛选、聚合、批量状态更改、
Account.objects.create_trial()这样的替代构造函数)。这是表级逻辑的首选位置——这是 Django 自己的建议。大多数“模型变得臃肿”的压力,实际上都是应放在这里而非实例上的表级逻辑。 -
(b) 内聚的模型 mixin,或有界上下文模型拆分。 适用时机: 单个模型上的信号是 混合领域 / 按职责统计方法数量——模型承担了多项内聚的工作。
- 首选做法:将每个内聚的功能簇提取为一个抽象模型 mixin(
BillingMixin、AuditMixin),让每项关注点都成为独立、易读的单元,同时保持表仍然只有一行。Mixin 必须真正内聚——只装“剩余方法”的 mixin,不过是多拆了几个文件的上帝对象。 - 更进一步:如果这些关注点实际上属于_独立的聚合_,就拆分模型(通常也拆分应用),沿有界上下文划分——例如,将
Subscription/Invoice从臃肿的Account中拆出。按领域能力拆分应用才是真正的扩展方式;参见上文“以领域为先的应用”。
- 首选做法:将每个内聚的功能簇提取为一个抽象模型 mixin(
-
(c) 无状态处理器函数(模块级),或专用处理器类。 适用时机: 工作需要协调多个模型,且没有适合承担该工作的单一聚合根——也就是跨聚合 /
save()负责协调的信号。这是“拆解过度复杂方法”的做法(Bennett 的后续文章),而不是把一团乱麻挪个地方。- 默认形式:应用中的无状态模块级函数(例如
app/operations.py/app/processors.py),将参与的对象作为参数,在一个transaction.atomic()中完成流程编排,并通过on_commit安排副作用。没有状态需要保存时,优先使用函数而非类(Luke Plant)。 - 只有确实需要在协调过程中跨步骤保留状态(例如构建器、多阶段导入)时,才使用专用处理器 / 处理程序类。按操作命名(
OrderCheckoutProcessor),默认保持无状态,并将不变量留在它所驱动的模型上——处理器负责安排流程,模型仍负责正确性。 - 这一级负责_协调_,而非领域服务层:它调用模型 / 管理器方法,不会重新实现属于这些方法的业务规则。
- 默认形式:应用中的无状态模块级函数(例如
-
(d) 服务层——最后手段,仅用于协调外部 API。 适用时机: 协调工作主要是与外部系统交互(Stripe、AWS、第三方 API),而这些调用的流程编排 / 错误处理 / 重试才是真正的复杂之处。此时,可以使用限定于该外部集成的
services.py。- 约束:
services.py不包含数据库业务逻辑 / 不变量——这些仍属于模型。服务负责协调外部调用,并将结果交给模型 / 管理器方法。 - 这一级特意放在阶梯顶端:通用的“所有业务逻辑都放进
services.py”层(HackSoft / 企业级模式)是一种确实有依据的风格,但不是本指南的默认做法——它用一个并行层替代了模型即 API 的方式,而对于大多数应用,上面的几级已经足够。只有在团队明确作出并记录决策时,才采用项目范围的服务层;不要只因某个模型变得臃肿,就条件反射般地使用它。
- 约束:
相关阅读(selectors.py)。 不受写入侧阶梯限制,复杂的跨模型读取操作(报表 / 仪表板)如果不适合放在任何一个 QuerySet 上,可以放在 selectors.py 中并返回带类型的 DTO。Selector 只读,绝不修改数据。
本原则的参考资料
- Django 文档 — Managers:行级逻辑 → Model 方法,表级逻辑 → Manager/QuerySet 方法:https://docs.djangoproject.com/en/6.1/topics/db/managers/#adding-extra-manager-methods
- James Bennett — “反对 Django 中的服务层”及后续文章(“关于服务层的更多讨论”):https://www.b-list.org/weblog/2020/mar/16/no-service/ · https://www.b-list.org/weblog/2020/mar/23/still-no-service/
- James Bennett — “Litestar 值得一看”(再次明确了反对服务层的立场,尤其针对 Django):https://www.b-list.org/weblog/2025/aug/06/litestar/
- DabApps — “Django 模型、封装与数据完整性”(绝不要从外部写入字段或调用
save()):https://www.dabapps.com/insights/django-models-and-encapsulation/ - Luke Plant — “Django 视图——正确的做法”(优先使用函数而非类,反对过度抽象):https://spookylukey.github.io/django-views-the-right-way/
- HackSoft Django Styleguide — 服务层 /
selectors.py阵营(第 (d) 级风格,将其作为一种选择,而非默认方案):https://github.com/HackSoftware/Django-Styleguide - Carlton Gibson / Vinta — “超越胖模型与服务层的二元对立”(将模块级函数作为折中方案):https://www.vintasoftware.com/lessons-learned/djangoservicelayersbeyondfatmodelsvsenterprisepatterns
审查清单
领域规则 / 胖模型
- 业务规则放在模型 / 查询集上(默认归属)
- 边界层只负责编排
- 不从模型 + 管理器方法之外写入字段、调用
save()或执行批量操作 - 不出现上帝对象:如果一个胖模型涉及多个混杂领域,或某个
save()/ 方法驱动了多模型编排,就应沿升级路径处理(查询集 → mixin/拆分 → 处理器函数/类 → 仅用于外部 API 的服务),而不是继续膨胀
ORM 性能
- 没有 N+1 查询
- 查询集按序列化器 / 模板的需求构造
- 已审查约束 / 索引
事务与副作用
- 多步骤写入流程由
atomic()包裹 - 副作用通过
on_commit安排
迁移
- 强制线性迁移(每个应用只有一个叶节点 — 使用
django-linear-migrations或手动审查) -
makemigrations --check通过 - 迁移安全且便于审查
- 删除迁移文件的合并迁移会保留
replaces,直到每个数据库都已记录该迁移,并且会在执行migrate前拒绝处于中间状态的数据库(如果没有replaces:说明备份、行数、架构差异、重写和回滚计划)— 参见references/squashing-migrations.md - 注释即代码(遵循 ac-python):不写重复签名的文档字符串,也不写复述 RunPython 函数体的行内注释 — 数据迁移的意图应由其函数名表达,而不是靠 6 行文档字符串
任务
- 幂等
- 在提交后入队
安全
- 尽早检查授权
- 保留 CSRF
- 正确配置 CSP
架构健康度(模块级)
审查差异所涉及的完整文件,而不只是改动的行:
- 默认采用胖模型:业务逻辑放在模型 / 查询集上,而非视图 / 命令 / CLI 中
- 视图和管理命令只负责编排 — 不包含不变量或工作流规则
- 不出现上帝模块(单个文件混合互不相关的关注点),也不出现上帝对象(单个模型不断累积无关关注点 / 多模型编排)
- 多模型编排放在无状态的处理器函数 / 类中(或聚合根方法上),而不是硬塞进某个模型的
save() -
pyproject.toml中除python-boilerplate基线以外,没有抑制复杂度规则的配置(C901、PLR09xx) - 如果存在
services.py,其范围仅限外部 API 编排(Stripe、AWS),不包含数据库业务逻辑 — 项目级服务层应是有记录的团队决策,而非默认方案
测试
- 对可空 / 可选数据使用 traits
- 测试明确说明前置条件
通过 prek 强制执行
上述可确定性检查的测试约定已作为 prek 钩子集成到此仓库(.pre-commit-hooks.yaml + ac-django/rules/)中。AST 结构检查采用 ast-grep YAML 规则;pyproject.toml 表面(ast-grep 无法解析,因为它不支持 TOML 语言)则由一个精简的独立 Python 钩子检查。使用这些规则的仓库通过 URL 和 rev 引用它们:
- repo: https://github.com/souliane/skills
rev: <commit-sha>
hooks:
- id: ac-django-no-pytest-django-db
- id: ac-django-testcase-no-pytest-parametrize
- id: ac-django-no-complexity-suppressions
- id: ac-django-no-pyproject-complexity
| 钩子 id | 引擎 | 失败条件 |
|---|---|---|
ac-django-no-pytest-django-db | ast-grep | 使用了任何 pytest.mark.django_db——包括 @pytest.mark.django_db 装饰器,以及模块级的 pytestmark = pytest.mark.django_db(或列表)。请使用 django.test.TestCase。 |
ac-django-testcase-no-pytest-parametrize | ast-grep | TestCase 子类中的方法使用了 @pytest.mark.parametrize(pytest 会静默忽略此处的装饰器)——请使用 unittest_parametrize。模块级 pytest 风格的函数不会被标记。 |
ac-django-no-complexity-suppressions | ast-grep | 源码或测试中有 # noqa: C901/PLR09xx 注释 |
ac-django-no-pyproject-complexity | 独立钩子 | pyproject.toml 的 ruff lint.ignore / lint.extend-ignore / lint.per-file-ignores 列表中有 C901/PLR09xx 条目 |
ast-grep 固定使用 0.44.1 版本。 包装器(ac-django/rules/astgrep_scan.py)会在 PATH 中存在 uv 时,通过 uvx --from ast-grep-cli==0.44.1 ast-grep 以隔离方式解析该版本(无需系统安装);否则回退到系统的 ast-grep。
这些检查采用默认失败、内联豁免的方式。 如果没有任何豁免,所有违规都会导致失败(新接入的仓库会立即受到全面强制检查)。没有基线文件,也没有数量上限——现有违规会在原处获得豁免:
- ast-grep 规则——在违规行上方添加 ast-grep 原生的退出检查注释:
# ast-grep-ignore[<rule-id>](例如# ast-grep-ignore[ac-django-no-pytest-django-db])。它只会针对该节点屏蔽该规则;每个新出现的(未豁免)违规仍会导致失败。 ac-django-no-pyproject-complexity——在使用方仓库的钩子args中,用--grandfather <code>@<location>的形式逐一列出每个现有条目(例如--grandfather [email protected]/**/*.py)。任何未列出的新条目都会导致失败;随着代码改进,列表只会缩短。
这样无需数据文件,也能保持递进规则“只收紧”的特性:豁免记录存放在违规所在的位置(内联注释或使用方自己的 .pre-commit-config.yaml),因此永远不会与代码树脱节。
仅供审查的约定(有意不设为强制门槛)。 Factory Boy 的使用以及通过 setUpTestData() 共享测试设置是团队约定,但为它们添加钩子会产生太多误报——存在许多合理的非工厂 create() 调用,也有许多需要逐个测试设置的 setUp()。这些内容仍列在审查清单中,不作为阻断性钩子。
上一版:Django 5.2 LTS
Django 5.2 是 LTS 版本,将在延长支持期内获得支持,直到 2028 年 4 月,因此仍有很多生产代码在使用它。以上内容均以 Django 6 为目标。本节说明两者的差异。
Django 6 有而 5.2 没有的功能
| 功能 | Django 6 | 5.2 中的替代方案 |
|---|---|---|
| 模板局部片段 | 原生 {% partialdef %} / {% partial %} | django-template-partials |
| 后台任务 | 原生 django.tasks(@task、.enqueue()) | Celery / Huey / RQ |
| CSP | 原生 CSP 中间件 + SECURE_CSP | django-csp |
| 获取模式(6.1+) | QuerySet.fetch_mode() | 显式使用 select_related() / prefetch_related() |
数据库级 on_delete(6.1+) | DB_CASCADE / DB_SET_NULL / DB_SET_DEFAULT | 仅支持 Python 级的 on_delete |
| 多个邮件发送器(6.1+) | MAILERS | 扁平的 EMAIL_* 设置 |
| 压缩已压缩的迁移 | squashmigrations 接受已压缩的迁移 | 先将较早的压缩迁移转换为普通迁移(删除其被替代的文件,移除 replaces) |
升级
- 进行任何手动重构之前,先运行
django-upgrade。 - 使用兼容层时,明确保留升级 TODO:
# TODO(Django6): switch @shared_task -> @task and .delay() -> .enqueue()# TODO(Django6): remove {% load partials %} (partials become native)# TODO(Django6): replace django-csp with built-in CSP middleware
- Django 5.2 是支持 Python 3.10 和 3.11 的最后一个版本系列;Django 6.x 需要 Python 3.12+。