使用django-guardian限制仅父模型所有者可在Django通用视图下创建子模型
基于django-guardian的规范实现方案
核心思路
复用你已有的django-guardian对象权限体系,通过guardian提供的对象权限校验混入类,在请求进入视图早期完成Parent归属校验,替代手动写的权限判断逻辑,和Parent模型CRUD的权限控制逻辑保持统一。
前置准备
确保你在创建Parent实例时,已经给创建者分配了对应实例的manipulate权限,示例代码如下(加在Parent的CreateView的form_valid逻辑中即可):
from guardian.shortcuts import assign_perm # Parent实例创建成功后添加 assign_perm("你的应用名.manipulate_parent", request.user, parent实例)
注意Django会自动给模型自定义权限的codename加上_模型名小写后缀,所以你定义的manipulate权限对应的完整codename是manipulate_parent。
优化后的完整代码
views.py
from django.views.generic.edit import CreateView from django.contrib.auth.mixins import LoginRequiredMixin from django.urls import reverse_lazy from django.forms import ModelForm from guardian.mixins import ObjectPermissionRequiredMixin from . import models class CreateChildForm(ModelForm): class Meta: fields = ('name',) # 直接移除parent字段,彻底避免表单篡改风险 model = models.Child class CreateChild(LoginRequiredMixin, ObjectPermissionRequiredMixin, CreateView): model = models.Child form_class = CreateChildForm success_url = reverse_lazy('home') # 配置需要校验的权限,替换为你自己的应用名 permission_required = "你的应用名.manipulate_parent" # 无权限时直接返回403,不需要重定向到登录页(已通过LoginRequiredMixin校验登录状态) return_403 = True def get_permission_object(self): # 返回要校验权限的Parent实例,guardian会自动完成权限校验 return models.Parent.objects.get(uuid=self.kwargs['uuid']) def form_valid(self, form): # 视图层强制绑定parent,完全避免参数篡改风险 form.instance.parent = self.get_permission_object() return super().form_valid(form)
逻辑说明
- 权限校验逻辑前置:
ObjectPermissionRequiredMixin会在视图执行任何业务逻辑前完成权限校验,无权限直接返回403,比你之前在get_form_kwargs里校验的逻辑更靠前,符合权限控制的最佳实践。如果用户传入的UUID不存在,查询Parent时会自动抛出404错误,符合预期。 - 彻底规避参数篡改风险:直接从表单字段中移除了parent字段,完全在视图层赋值,不会出现用户篡改隐藏字段提交的问题。
- 权限体系统一:和你之前Parent CRUD用的django-guardian逻辑完全一致,后续权限调整只需要修改权限定义,不需要改业务校验代码。
内容的提问来源于stack exchange,提问作者buttery
相关产品推荐
相关产品推荐

