You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:55:30