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

如何在Django Wagtail中为指定ModelAdmin添加类似页面权限的独立权限

Wagtail ModelAdmin实现科目分组对象级权限方案

要实现和Wagtail原生「页面权限」逻辑一致的「指定用户组仅访问授权科目」效果,属于对象级行权限控制,无法仅靠Django默认的模型级增删改权限完成,需要从权限关联、配置面板、访问拦截三层实现,具体步骤如下:

1. 给Subject模型增加组权限关联字段

首先在科目模型上新增多对多字段,关联Django内置的Group模型,存储每个科目对应的可访问用户组:

from django.db import models
from django.contrib.auth.models import Group

class Subject(models.Model):
    name = models.CharField(max_length=100, verbose_name="科目名称")
    # 保留你原有字段,比如科目编码、描述、所属学院等
    # 新增权限关联字段
    allowed_groups = models.ManyToManyField(
        Group,
        blank=True,
        related_name="accessible_subjects",
        verbose_name="可访问用户组"
    )

    def __str__(self):
        return self.name

字段添加完成后执行python manage.py makemigrations和python manage.py migrate完成数据库迁移。

2. 在用户组编辑页嵌入科目权限配置面板

要和原生页面权限的交互保持一致,直接在用户组新增/编辑页提供科目授权入口,不需要在科目编辑页反向选组,通过Wagtail内置的register_group_permission_panel钩子实现,在对应app的wagtail_hooks.py中添加如下代码:

from django import forms
from wagtail import hooks
from .models import Subject

class SubjectPermissionForm(forms.Form):
    subjects = forms.ModelMultipleChoiceField(
        queryset=Subject.objects.all(),
        required=False,
        label="可访问科目",
        widget=forms.CheckboxSelectMultiple,
        help_text="勾选该用户组可访问的科目,不勾选则默认无任何科目访问权限"
    )

    def __init__(self, *args, **kwargs):
        group = kwargs.pop("group", None)
        super().__init__(*args, **kwargs)
        # 编辑已有用户组时,回填已分配的科目权限
        if group:
            self.fields["subjects"].initial = Subject.objects.filter(
                allowed_groups=group
            ).values_list("id", flat=True)

    def save(self, group):
        # 保存时先清空旧的关联关系,再写入新的权限配置
        Subject.allowed_groups.through.objects.filter(group=group).delete()
        for subject in self.cleaned_data["subjects"]:
            subject.allowed_groups.add(group)

@hooks.register("register_group_permission_panel")
def register_subject_permission_panel():
    return SubjectPermissionForm

代码添加完成后,打开后台用户组编辑页,就能看到和原生页面权限区块位置一致的科目权限配置区,交互逻辑完全对齐。

3. 配置ModelAdmin访问拦截规则

配置面板仅负责权限数据存储,还需要在ModelAdmin层做三层拦截:对无权限用户隐藏菜单入口、列表页只展示当前用户有权限访问的科目、拦截直接输入URL的越权访问。
在wagtail_hooks.py中修改原有的SubjectAdmin配置:

from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from django.core.exceptions import PermissionDenied

class SubjectAdmin(ModelAdmin):
    model = Subject
    menu_label = "科目管理"
    menu_icon = "book"
    list_display = ("name",)
    search_fields = ("name",)
    # 替换成你自己的app名称
    app_label = "your_app_name"

    def has_module_permission(self, request):
        # 超级用户默认放行
        if request.user.is_superuser:
            return True
        # 无任何科目访问权限的用户,直接隐藏科目管理菜单
        user_groups = request.user.groups.all()
        has_base_view_perm = request.user.has_perm(f"{self.app_label}.view_subject")
        accessible_count = Subject.objects.filter(allowed_groups__in=user_groups).count()
        return has_base_view_perm and accessible_count > 0

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if request.user.is_superuser:
            return qs
        # 列表页仅返回当前用户所属组有权限访问的科目
        user_groups = request.user.groups.all()
        return qs.filter(allowed_groups__in=user_groups).distinct()

    def get_object(self, request, object_id, to_field):
        obj = super().get_object(request, object_id, to_field)
        if request.user.is_superuser:
            return obj
        # 校验单条对象访问权限,防止URL越权
        user_groups = request.user.groups.all()
        if not obj.allowed_groups.filter(id__in=user_groups).exists():
            raise PermissionDenied("你没有访问该科目的权限")
        return obj

# 如果你之前已经注册过旧的SubjectAdmin,先注销再注册新的
modeladmin_register(SubjectAdmin)

4. 权限粒度细化(可选)

如果需要和页面权限一样区分查看、添加、编辑、删除的操作权限,直接复用Django原生的模型级权限即可:

  • 先给对应用户组分配Subject模型的新增、编辑、删除基础权限,作为操作前置校验
  • 再通过上述对象级权限规则,控制用户组具体可以对哪些科目执行已授权的操作

如果你使用的是Wagtail 3.0+版本的Snippet管理体系而非旧版ModelAdmin,核心逻辑完全一致,只需要把上述查询集过滤、权限校验的方法替换为SnippetViewSet对应的钩子方法即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 07:12:28