commit=False 让你在保存前补几个服务端字段,但它有一个经典的坑:多对多。这篇看它到底做了什么、没做什么,并复现那个不会报错的数据丢失。
INSERT INTO "lab_post" ("title", "owner") VALUES ('t1', '') RETURNING "lab_post"."id"
BEGIN
SELECT "lab_tag"."id" AS "id" FROM "lab_tag" INNER JOIN "lab_post_tags" ON ("lab_tag"."id" = "lab_po
INSERT OR IGNORE INTO "lab_post_tags" ("post_id", "tag_id") VALUES (1, 1), (1, 2)
COMMIT
tags = ['a', 'b']普通路径:form.save() 一步到位。
save(commit=False) 发出的查询数: 0 p.pk = None 库里 Post 数量: 0
p.save():
INSERT INTO "lab_post" ("title", "owner") VALUES ('t2', 'me') RETURNING "lab_post"."id"
此时 tags = [] ← 多对多还没写
f.save_m2m():
BEGIN
SELECT "lab_tag"."id" AS "id" FROM "lab_tag" INNER JOIN "lab_post_tags" ON ("lab_tag"."id" = "lab_po
INSERT OR IGNORE INTO "lab_post_tags" ("post_id", "tag_id") VALUES (2, 1), (2, 2)
COMMIT
之后 tags = ['a', 'b']
form 上有 save_m2m 属性是因为 save(commit=False) 把它挂上去了: True返回的是一个还没有主键的对象,所以多对多关系此刻不可能写。Django 把「稍后补写多对多」的动作打包成 form.save_m2m。
忘了 save_m2m():库里这个 Post 的 tags = [] (用户选的 c 丢了,没有任何报错) 没调用 save(commit=False) 就用 save_m2m: AttributeError: 'PF' object has no attribute 'save_m2m'
这个 bug 只在使用多对多的表单上出现,而且不会抛异常。代码审查时看到 commit=False 就要找 save_m2m()。
owner = request.user tags = ['a']
如果只是想补一个字段,还有另一种写法:重写 form.save() 或在 form.instance 上先设置字段再 form.save()(本文没有测)。