Django Guardian开发拉黑功能时assign_perm报权限查询不存在错误
问题根因
permission matching query does not exist错误以及后续逻辑不生效,核心是4个写法和设计问题:
- 权限标识符格式错误:Django(包括Django Guardian)识别自定义权限必须带所属应用前缀,格式为
<app_label>.<permission_codename>,直接传cant_view_profile短名会导致ORM查不到auth_permission表中对应记录。Admin后台能正常分配是因为后台自动补全了app前缀,不存在这个问题。 - 权限体系关联错误:Profile模型中自定义的
group多对多字段和Django内置权限体系无关,Django的用户组、权限逻辑是绑定在默认User模型上的,给Profile实例加组完全不会被权限框架识别。 assign_perm参数逻辑颠倒:Guardian的assign_perm方法签名为assign_perm(权限名, 授权主体(用户/用户组), 权限绑定的对象),当前代码写反了授权主体和绑定对象,实际是给blockedusers组分配了针对当前登录用户的权限,和“限制被拉黑用户访问主页”的需求完全不符。- 权限设计逻辑错误:用全局用户组绑定权限的思路,会导致所有进入blockedusers组的用户无法访问全站所有用户的主页,不符合拉黑功能“仅禁止访问拉黑者本人主页”的对象级限制需求。
代码修复步骤
- 先确认自定义权限已经同步到数据库,执行迁移命令:
python manage.py makemigrations python manage.py migrate
- 删掉Profile模型中自定义的
group字段,直接使用Django User模型内置的groups关联即可,不需要重复定义。 - 修正视图中的权限标识符和参数逻辑,注意把
profiles替换成Profile模型所在的实际app名称:
def block_user_view(request, id): if request.method == "POST": blocked_group = Group.objects.get(name="blockedusers") # 拿到被拉黑用户对应的User对象,而非Profile实例 blocked_user = Profile.objects.get(id=id).user current_user_profile = request.user.profile # 给blockedusers组分配「不能查看当前用户主页」的对象级权限,绑定当前用户的Profile实例 assign_perm("profiles.cant_view_profile", blocked_group, current_user_profile) # 把被拉黑用户加入blocked组 blocked_user.groups.add(blocked_group) # 权限校验 print(blocked_user.has_perm("profiles.cant_view_profile", current_user_profile)) return render(request, "profiles/blockuser.html", {})
- 在用户主页访问视图中增加权限校验,被限制的用户直接返回403:
from django.http import HttpResponseForbidden def profile_detail_view(request, user_id): profile = Profile.objects.get(user_id=user_id) # 校验当前用户是否有查看该主页的权限 if request.user.has_perm("profiles.cant_view_profile", profile): return HttpResponseForbidden("你已被该用户拉黑,无法访问主页") return render(request, "profiles/detail.html", {"profile": profile})
更轻量的替代实现方案
如果拉黑功能不需要复杂的权限组合,完全没必要引入Guardian,直接用多对多字段存黑名单即可,逻辑更简单、性能更好:
- 在Profile模型中增加黑名单关联字段:
class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) prof_pics = models.ImageField(null=True, blank=True, upload_to='images/') friends = models.ManyToManyField(User, blank=True, related_name="friend") bio = models.TextField(blank=True) # 存储所有被当前用户拉黑的用户 blocked_users = models.ManyToManyField(User, blank=True, related_name="blocked_by") def __str__(self): return str(self.user)
- 拉黑视图直接操作关联字段即可,不需要处理权限和用户组:
def block_user_view(request, id): if request.method == "POST": blocked_user = User.objects.get(profile__id=id) request.user.profile.blocked_users.add(blocked_user) return render(request, "profiles/blockuser.html", {})
- 主页访问时直接判断黑名单:
from django.http import HttpResponseForbidden def profile_detail_view(request, user_id): profile = Profile.objects.get(user_id=user_id) # 当前访问用户在黑名单内则直接拦截 if request.user in profile.blocked_users.all(): return HttpResponseForbidden("你已被该用户拉黑,无法访问主页") return render(request, "profiles/detail.html", {"profile": profile})
该方案没有额外的权限表查询逻辑,不需要维护用户组,后续扩展拉黑后的互动限制(比如禁止评论、禁止发私信)也只需要加一层黑名单判断即可,是中小站点实现拉黑功能的首选方案。
内容的提问来源于stack exchange,提问作者Emmanuel C. O Peters
相关产品推荐
相关产品推荐

