Django中UserAdmin的save_model首次保存未生效,二次保存才生效?
问题分析与解决方案
这个问题的核心是多对多字段的保存时机和你处理权限的顺序不匹配,导致第一次保存时用了旧的项目关联数据来更新权限。
为什么会出现这个问题?
在Django Admin中,处理包含多对多字段的表单时,对象本身的保存(obj.save())和多对多关系的保存是分开执行的:
- 首先保存对象的基础字段(比如用户名、邮箱等)
- 然后通过
form.save_m2m()来完成多对多关联数据(也就是你的workingproject字段)的保存
而你的代码逻辑顺序出了问题:
obj.save() # 仅保存了对象基础字段,多对多关联还没更新 projects = obj.workingproject.all() # 这里拿到的是修改前的旧项目列表 # 用旧数据处理权限 remove_perm("view_project", obj, allprojects) assign_perm("view_project", obj, projects) obj.save() # 多余的保存,对权限设置无意义
第一次保存时,你在多对多关系完成更新前就获取了项目列表,导致权限还是基于旧的关联设置。而第二次保存时,多对多关系已经在上一次流程中完成了更新,这时候获取的是新的项目列表,权限设置自然就符合预期了。
修复方案
调整代码顺序,先确保多对多关系完全保存,再处理权限。最稳妥的方式是调用父类的save_model方法,它会帮你处理好所有表单保存逻辑(包括多对多字段):
class UserAdmin(BaseUserAdmin): def save_model(self, request, obj, form, change): # 先让父类完成对象和多对多字段的全部保存 super().save_model(request, obj, form, change) # 此时obj.workingproject已经是更新后的关联数据 allprojects = Project.objects.all() projects = obj.workingproject.all() remove_perm("view_project", obj, allprojects) assign_perm("view_project", obj, projects) # 无需再调用obj.save(),因为权限是django-guardian单独存储的,和用户对象本身无关
额外优化建议
- 移除最后多余的
obj.save():权限是独立存储的,保存用户对象不会影响权限设置,这一步完全没必要。 - 优化权限更新逻辑:不用对所有项目执行权限移除操作,只处理需要变更的部分,能提升性能:
# 获取用户当前已拥有view_project权限的项目 existing_projects = Project.objects.filter( userobjectpermission__user=obj, userobjectpermission__permission__codename='view_project' ) # 移除不在新关联列表中的项目权限 remove_perm( "view_project", obj, existing_projects.exclude(id__in=projects.values_list('id', flat=True)) ) # 添加新关联列表中未拥有的权限 assign_perm( "view_project", obj, projects.exclude(id__in=existing_projects.values_list('id', flat=True)) )
内容的提问来源于stack exchange,提问作者Rubick
相关产品推荐
相关产品推荐

