Django中如何基于模型实例字段动态生成自定义权限
结论
该需求完全可以实现,你遇到报错的核心原因是用错了权限的定义位置。
问题原因
Meta类中的permissions属性是模型类级别的配置,Django会在项目启动、加载模型定义的阶段就解析该配置,此时还没有任何Institute类的实例对象,self指向的实例根本不存在,自然会报NameError。permissions原本的设计是用来定义整个模型通用的全局固定权限,不支持动态绑定实例字段值。
实现方案
和你动态创建用户组的逻辑一致,把动态权限的创建逻辑放到实例的save方法中即可,此时实例已经完成保存,可以正常获取self.name等字段值,你需要调用Django原生的Permission和ContentType模型来手动创建权限。
完整代码示例
from django.contrib.auth.models import Group, Permission, ContentType from django.db import models from django.db.models import SET_NULL # 提前导入你的Address模型 class Institute(models.Model): CATEGORY=( ('play', 'play'), ('High school', 'High school'), ('College', 'College'), ) eiin = models.CharField(max_length=50, blank=True, null= True) name = models.CharField(max_length=100) address = models.ForeignKey(Address, on_delete=SET_NULL, blank=True, null=True) category = models.CharField(max_length=100, choices=CATEGORY) def save(self, *args, **kwargs): # 先判断是否为新建实例、name字段是否修改,避免重复操作 is_new = self._state.adding old_name = None if not is_new: old_name = Institute.objects.get(pk=self.pk).name super().save(*args, **kwargs) # 动态创建/更新用户组 group_suffixes = ["_students", "_teachers", "_controller", "_employees", "_guardians"] if is_new: # 新建实例直接创建对应组 for suffix in group_suffixes: Group.objects.create(name=f"{self.name}{suffix}") elif old_name != self.name: # name修改时同步更新所有关联组的名称 for suffix in group_suffixes: Group.objects.filter(name=f"{old_name}{suffix}").update(name=f"{self.name}{suffix}") # 动态创建/更新自定义权限 # 获取当前模型的ContentType,用于绑定权限归属 ct = ContentType.objects.get_for_model(Institute) # 定义权限规则:(codename后缀, 权限展示名称) permission_rules = [ ("_can_edit_address", "can edit address of institute"), ("_can_add_eiin", "can add eiin of institute") ] for codename_suffix, perm_name in permission_rules: full_codename = f"{self.name}{codename_suffix}" # 不存在才创建,避免重复生成权限 perm, _ = Permission.objects.get_or_create( codename=full_codename, content_type=ct, defaults={"name": perm_name} ) # 可按需把权限分配给对应角色组,比如给管理员组分配编辑权限 controller_group = Group.objects.get(name=f"{self.name}_controller") controller_group.permissions.add(perm) # name修改时同步更新旧权限的codename if old_name and old_name != self.name: for codename_suffix, _ in permission_rules: old_codename = f"{old_name}{codename_suffix}" Permission.objects.filter(codename=old_codename, content_type=ct).update( codename=f"{self.name}{codename_suffix}" ) # 删掉原来Meta里的permissions配置即可 class Meta: pass
权限校验方式
后续要判断用户是否持有某个学院的对应权限,直接用以下代码即可:
# 示例:判断当前用户是否可以修改xx学院的地址 user.has_perm(f"{institute_instance.name}_can_edit_address")
补充说明
如果需要删除学院实例时同步清理对应的用户组和权限,可重写delete方法,批量删除关联的Group和Permission对象即可。
内容的提问来源于stack exchange,提问作者md jakaria
相关产品推荐
相关产品推荐

