bigywater.tech
Django · 66

ModelForm 的多对多字段保存

ModelForm 保存多对多是先清空再重建吗?取消全部勾选会怎样?有额外字段的 through 表能用表单保存吗?这篇看每种情形的 SQL。

差集
保存时只删除被去掉的、只插入新增的关系,没变的不动
清空
编辑时一个都不勾选 = 清空多对多(blank=True 时)
through 额外字段
表单保存时 role 只能取默认值

保存时的 SQL

实测 · 本机执行输出
   UPDATE "lab_post" SET "title" = 'p' WHERE "lab_post"."id" = 1
   BEGIN
   SELECT "lab_tag"."id" AS "id" FROM "lab_tag" INNER JOIN "lab_post_tags" ON ("lab_tag"."id" = "lab_post_tags"."tag_id") W
   DELETE FROM "lab_post_tags" WHERE ("lab_post_tags"."post_id" = 1 AND "lab_post_tags"."tag_id" IN (1))
   INSERT OR IGNORE INTO "lab_post_tags" ("post_id", "tag_id") VALUES (1, 3)
   COMMIT
  之后 tags = ['b', 'c']

并发场景下这比「先全删再全建」更友好:没变的关系行不会被删除又重建(不会短暂消失)。

空提交与必填

实测 · 本机执行输出
is_valid = True
  没提交 tags 字段(等同于全部取消勾选)后 tags = []
  required=True 时: {'tags': ['required']}

把「不勾选」当作「清空」是符合直觉的,但如果前端漏渲染了这个字段,保存就会清空用户已有的关系——所以 fields 白名单里的多对多字段一定要在模板里渲染。

校验与 queryset

实测 · 本机执行输出
含不存在的 id: {'tags': [('invalid_choice', {'value': 99999})]}
不在 queryset 里的 id(d): {'tags': ['invalid_choice']}

用 queryset 限制用户能关联到哪些对象(比如只能选自己的标签)。

自定义 through

实测 · 本机执行输出
TeamForm 字段: ['name', 'members']
Team.members 的 through 是自定义模型: Membership
  TeamForm 提交 members:is_valid = True
  save() 成功;SQL:
     INSERT INTO "lab_team" ("name") VALUES ('t2') RETURNING "lab_team"."id"
     BEGIN
     SELECT "lab_member"."id" AS "id" FROM "lab_member" INNER JOIN "lab_membership" ON ("lab_member"."id" = "lab_membership".
     SELECT "lab_membership"."member_id" AS "member" FROM "lab_membership" WHERE ("lab_membership"."member_id" IN (1) AND "la
     INSERT INTO "lab_membership" ("member_id", "team_id", "role") VALUES (1, 2, 'dev') RETURNING "lab_membership"."id"
     COMMIT
  Membership 里的行: [('m1', 'dev')] (role 用 default)

Django 允许对自定义 through 模型的多对多使用 add()/set(),前提是额外字段都有默认值(或用 through_defaults 指定)。

分步演示:多对多保存

ModelForm 多对多
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。SQLite
    • 没有验证:PostgreSQL 上同样的 SQL(INSERT ... ON CONFLICT DO NOTHING)、自引用多对多、through_defaults 的用法、大量选项时 ModelMultipleChoiceField 的性能
    京ICP备2026067502号-1京公网安备11010502063487号