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

Django:如何限制未授权用户访问CreateView创建里程碑页面

问题描述

需要限制未授权用户访问添加新里程碑的页面,仅允许项目所有者和项目经理(存储在Project的外键与多对多字段中)访问,其他用户应看到访问拒绝提示。当前使用CreateView的get_queryset方法无法实现拦截,因为该方法主要用于查询现有数据,不影响创建操作的权限校验流程。

现有代码

models.py

class Project(models.Model): 
    owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name="projects_as_owner", null=True)
    project_managers = models.ManyToManyField(User, related_name="projects_as_pm", blank=True)
    name = models.CharField(max_length=200)
    description = models.TextField(blank=True)
    date_of_insert = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name


class Milestone(models.Model):
    project_fk = models.ForeignKey(Project, related_name="milestones", on_delete=models.CASCADE)
    name = models.CharField(max_length=200)
    description = models.TextField(blank=True)
    date_of_insert = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name 

views.py(存在问题的类)

class NewMilestone(LoginRequiredMixin, generic.CreateView):
    model = Milestone
    fields = ['name', 'description']
    lookup_url_kwarg = 'p_id'
    template_name = 'main/new_milestone.html'

    # ... 两个可正常工作的函数,此处省略 ...

    def get_queryset(self):
        qs = super(NewMilestone, self).get_queryset()
        project = Project.objects.get(id=self.kwargs['p_id'])
        if(qs.filter(project_fk__owner=self.request.user).exists() or User.objects.filter(id=self.request.user.id).filter(projects_as_pm__id=project.id).exists()):
            return qs
        else:
            return Http404("You are not authenticated to this action")
解决方案

CreateView的get_queryset方法不适合做创建操作的权限校验,它仅负责返回用于展示的查询集,无法拦截创建请求。可以通过以下两种方式实现需求:

方法一:重写dispatch方法

dispatch是视图处理请求的入口方法,在此处做权限校验可直接拦截所有未授权请求:

from django.http import HttpResponseForbidden

class NewMilestone(LoginRequiredMixin, generic.CreateView):
    model = Milestone
    fields = ['name', 'description']
    lookup_url_kwarg = 'p_id'
    template_name = 'main/new_milestone.html'

    def dispatch(self, request, *args, **kwargs):
        # 获取当前请求对应的项目
        project = Project.objects.get(id=self.kwargs['p_id'])
        # 校验用户身份:项目所有者或项目经理
        is_owner = project.owner == request.user
        is_pm = request.user in project.project_managers.all()
        if not (is_owner or is_pm):
            return HttpResponseForbidden("您没有权限执行此操作")
        return super().dispatch(request, *args, **kwargs)

    # 重写form_valid,将里程碑关联到目标项目
    def form_valid(self, form):
        project = Project.objects.get(id=self.kwargs['p_id'])
        form.instance.project_fk = project
        return super().form_valid(form)

方法二:自定义权限Mixin(复用场景)

如果需要在多个视图中复用该权限逻辑,可以自定义权限Mixin:

from django.http import HttpResponseForbidden

class ProjectPermissionMixin:
    def has_permission(self):
        project = Project.objects.get(id=self.kwargs['p_id'])
        return project.owner == self.request.user or self.request.user in project.project_managers.all()

    def dispatch(self, request, *args, **kwargs):
        if not self.has_permission():
            return HttpResponseForbidden("您没有权限执行此操作")
        return super().dispatch(request, *args, **kwargs)

# 视图继承自定义Mixin
class NewMilestone(LoginRequiredMixin, ProjectPermissionMixin, generic.CreateView):
    model = Milestone
    fields = ['name', 'description']
    lookup_url_kwarg = 'p_id'
    template_name = 'main/new_milestone.html'

    def form_valid(self, form):
        project = Project.objects.get(id=self.kwargs['p_id'])
        form.instance.project_fk = project
        return super().form_valid(form)

注意事项

  • 必须重写form_valid方法,将创建的里程碑关联到目标项目,否则会因project_fk字段为空触发数据库错误。
  • 可根据需求将HttpResponseForbidden替换为自定义403页面,比如使用render返回自定义模板。

内容的提问来源于stack exchange,提问作者Komnatow

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 01:31:01