Django中限制用户访问关联模型的权限控制问题
嘿,这个多层关联模型的权限控制问题我在项目里碰到过好几次,给你几个靠谱的解决方案,你可以根据自己的项目复杂度选:
方案1:自定义通用权限类(最推荐,复用性拉满)
既然所有关联模型最终都能追溯到Bot,我们可以写一个通用的权限类,自动沿着外键链找到对应的Bot,再校验归属。这样所有关联视图都能直接复用,不用重复写逻辑。
先写权限类:
from rest_framework.permissions import BasePermission from .models import ContentBlock, Campaign, Subscriber, CampaignDelivery class IsOwnerOfBotRelatedModel(BasePermission): """校验当前用户是否是关联模型所属Bot的所有者""" def has_object_permission(self, request, view, obj): bot = self._trace_back_to_bot(obj) return bot is not None and bot.user == request.user def _trace_back_to_bot(self, obj): # 配置每个模型到Bot的属性链,子模型就用双下划线连起来 model_bot_mapping = { ContentBlock: 'bot', Campaign: 'bot', Subscriber: 'bot', CampaignDelivery: 'campaign__bot', # 其他子关联模型按这个格式加就行 } attr_chain = model_bot_mapping.get(type(obj)) if not attr_chain: return None # 一步步沿着属性链找Bot current_obj = obj for attr in attr_chain.split('__'): current_obj = getattr(current_obj, attr, None) if current_obj is None: break return current_obj
然后在任何关联模型的视图里直接用这个权限类就行,比如:
from rest_framework.viewsets import ModelViewSet from .models import CampaignDelivery from .permissions import IsOwnerOfBotRelatedModel class CampaignDeliveryViewSet(ModelViewSet): queryset = CampaignDelivery.objects.all() permission_classes = [IsOwnerOfBotRelatedModel] # 其他视图配置...
这个方案的好处是一劳永逸,新增关联模型时只需要在model_bot_mapping里加一行配置就行。
方案2:重写视图的
get_queryset(适合小型项目) 如果你的关联模型不多,不想写额外的权限类,直接在每个视图里重写get_queryset,过滤出当前用户的Bot对应的实例就行。
比如Campaign的列表视图:
from django.views.generic import ListView from .models import Campaign class CampaignListView(ListView): model = Campaign def get_queryset(self): # 只返回当前用户Bot下的Campaign return super().get_queryset().filter(bot__user=self.request.user)
子关联模型比如CampaignDelivery:
class CampaignDeliveryListView(ListView): model = CampaignDelivery def get_queryset(self): return super().get_queryset().filter(campaign__bot__user=self.request.user)
这个方法简单直接,但缺点是每个视图都要写一遍,模型多了就会重复代码,维护起来麻烦。
方案3:用Django Guardian做细粒度权限(适合复杂权限场景)
如果你的项目除了“所有者”,还有其他角色需要访问(比如编辑、只读),可以用Django Guardian这个第三方库来实现更灵活的权限控制。
安装后,你可以在创建Bot时给用户分配权限,然后让关联模型自动继承Bot的权限。比如:
from guardian.shortcuts import assign_perm from .models import Bot, ContentBlock def create_bot(request): bot = Bot.objects.create(user=request.user, name="My Bot") # 给用户分配该Bot的查看/编辑权限 assign_perm('view_bot', request.user, bot) assign_perm('change_bot', request.user, bot) # 创建关联的ContentBlock时,自动给用户分配该内容块的权限 content_block = ContentBlock.objects.create(bot=bot, content="Hello") assign_perm('view_contentblock', request.user, content_block)
不过这个方案需要额外维护权限记录,适合权限需求复杂的项目,简单场景用前两个方案足够。
额外小技巧:防止非法创建关联模型
除了视图层面的权限控制,最好在模型保存时也做一层校验,避免有人通过API或者后台直接创建不属于自己的关联模型。你可以用信号来做:
from django.db.models.signals import pre_save from django.dispatch import receiver from django_currentuser.middleware import get_current_user from .models import ContentBlock @receiver(pre_save, sender=ContentBlock) def validate_contentblock_owner(sender, instance, **kwargs): # 用django-currentuser库获取当前操作的用户 current_user = get_current_user() if current_user and instance.bot.user != current_user: raise ValueError("你只能创建属于自己Bot的内容块!")
(注:需要先安装django-currentuser库来在信号里获取当前用户)
这样就能从源头防止非法数据的创建啦。
内容的提问来源于stack exchange,提问作者kurtgn
相关产品推荐
相关产品推荐

