自定义User类下迁移至django-guardian权限的问题
看起来你在把项目迁移到Guardian权限系统时遇到了自定义User类的识别问题,我来帮你梳理下规范的迁移步骤,以及解决guardian.exceptions.NotUserNorGroup错误的具体方法。
一、先确保Guardian能识别你的自定义User类
Guardian默认依赖Django的AUTH_USER_MODEL设置,所以第一步要确认你的项目已经正确配置了自定义User类:
在settings.py中指定AUTH_USER_MODEL
确保你的EmailUser已经被设置为项目的默认用户模型:AUTH_USER_MODEL = "your_app.EmailUser" # 替换成你的app名和模型名注意:这个设置最好在项目初始迁移前就配置好,如果是中途修改,可能需要重新初始化数据库(或者做复杂的迁移调整,所以尽量一开始就设置)。
确认EmailUser的继承结构
你的EmailUser需要继承自AbstractUser或者AbstractBaseUser,并且配置了必要的管理器(比如UserManager),确保它是Django认可的用户模型。Guardian的自动适配
只要AUTH_USER_MODEL配置正确,Guardian会自动识别你的自定义User类,不需要额外配置(除非你有特殊的自定义权限后端需求)。
二、规范的Guardian迁移流程
接下来是从无到有迁移到Guardian的标准步骤,避免踩坑:
1. 安装并配置Guardian
- 先安装Guardian:
pip install django-guardian - 在
INSTALLED_APPS中添加:INSTALLED_APPS = [ # ... 其他应用 "guardian", ] - 在
AUTHENTICATION_BACKENDS中添加Guardian的权限后端:AUTHENTICATION_BACKENDS = [ "django.contrib.auth.backends.ModelBackend", # 保留Django默认的后端 "guardian.backends.ObjectPermissionBackend", # 添加Guardian的对象权限后端 ]
2. 创建自定义权限(如果需要)
你已经完成了这一步,不过要注意:自定义权限的迁移文件应该基于你的业务模型来创建,比如给Post模型添加can_edit_private_posts权限,迁移文件示例:
from django.db import migrations def add_custom_permissions(apps, schema_editor): Permission = apps.get_model("auth", "Permission") ContentType = apps.get_model("contenttypes", "ContentType") post_content_type = ContentType.objects.get(app_label="your_app", model="post") Permission.objects.create( codename="can_edit_private_posts", name="Can edit private posts", content_type=post_content_type, ) class Migration(migrations.Migration): dependencies = [ ("your_app", "0001_initial"), # 依赖你的模型初始迁移 ] operations = [ migrations.RunPython(add_custom_permissions), ]
3. 在迁移中分配权限(解决你的错误)
你遇到的NotUserNorGroup错误,大概率是因为在迁移中直接导入了EmailUser类,而不是使用Django迁移API的apps.get_model来获取模型实例。迁移中应该用这种方式来避免模型引用问题:
from django.db import migrations def assign_permissions(apps, schema_editor): # 通过apps.get_model获取模型,避免直接导入 User = apps.get_model("your_app", "EmailUser") Permission = apps.get_model("auth", "Permission") # 如果是对象级权限,还要获取目标对象模型 Post = apps.get_model("your_app", "Post") # 获取要分配权限的用户 target_user = User.objects.get(email="user@example.com") # 获取要分配的权限 edit_perm = Permission.objects.get(codename="can_edit_private_posts") # 如果是对象级权限,获取目标对象 target_post = Post.objects.get(id=1) # 方式1:使用Guardian的shortcut函数 from guardian.shortcuts import assign_permission # 全局权限(不需要对象):assign_permission(edit_perm, target_user) # 对象级权限: assign_permission(edit_perm, target_user, target_post) # 方式2:直接操作Guardian的底层模型(如果方式1还是报错) # UserObjectPermission = apps.get_model("guardian", "UserObjectPermission") # UserObjectPermission.objects.create( # user=target_user, # permission=edit_perm, # content_object=target_post, # ) class Migration(migrations.Migration): dependencies = [ ("your_app", "0002_add_custom_permissions"), # 依赖创建权限的迁移 ("guardian", "0001_initial"), # 依赖Guardian的初始迁移 ] operations = [ migrations.RunPython(assign_permissions), ]
关键注意点:
- 绝对不要在迁移中直接
from your_app.models import EmailUser,必须用apps.get_model,因为迁移运行时模型的状态可能和当前代码不一致。 - 如果使用
assign_permission还是报错,可以尝试直接创建UserObjectPermission实例,绕开Guardian的类型检查(不过只要AUTH_USER_MODEL配置正确,方式1应该就能正常工作)。
三、额外的注意事项
- 如果是中途迁移到Guardian,要确保现有用户的权限已经被正确迁移(比如把Django的组权限映射到Guardian的权限)。
- 测试权限分配是否生效:可以在视图或shell中用
guardian.shortcuts.get_perms来验证用户是否拥有指定权限。 - 如果你需要给匿名用户分配权限,Guardian支持
AnonymousUser,直接传入即可。
内容的提问来源于stack exchange,提问作者David R.

