使用django-guardian实现django-admin的行级权限控制的方法

yizhihongxing

下面是使用django-guardian实现django-admin的行级权限控制的方法的完整攻略。

什么是django-guardian

django-guardian是一个用于Django的第三方插件,它提供一种简单的方式来将对象级别的访问控制系统嵌入到你的应用程序中。

django-guardian安装

您可以使用pip安装django-guardian:

pip install django-guardian

然后将guardian添加到INSTALLED_APPS中:

INSTALLED_APPS = [
    # ...
    'guardian',
    # ...
]

并且在MIDDLEWARE中添加GuardianMiddleware

MIDDLEWARE = [
    # ...
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'guardian.middleware.GuardianMiddleware',
    # ...
]

最后,在settings.py文件中添加以下设置:

AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
    'guardian.backends.ObjectPermissionBackend',
]

创建一个自定义的guardian后台

创建一个模型后台(ModelAdmin)是创建一个新的admin后台的第一步,这为您提供了一个管理特定模型的界面。 您可以针对不同的模型创建几个后台界面。

from django.contrib import admin

from .models import Post

@admin.register(Post)
class PostModelAdmin(admin.ModelAdmin):
    list_display = ('title', 'body', 'created_at', 'updated_at')

将Guardian添加到admin

需要创建一个自定义的admin后台,即一个子类GuardedModelAdmin(位于guardian.admin中),并将其传递给模型注册装饰器:

from django.contrib import admin
from guardian.admin import GuardedModelAdmin

from .models import Post

@admin.register(Post)
class PostModelAdmin(GuardedModelAdmin):
    list_display = ('title', 'body', 'created_at', 'updated_at')

第一种示例:给创建的文章添加用户级别的权限控制

现在,我们将为创建的文章添加用户级别的权限控制:

1.首先,在models.py中,定义一个组:

from django.contrib.auth.models import Group

class Writer(Group):

    class Meta:
        verbose_name = "Writer"
        verbose_name_plural = "Writers"

2.现在,我们需要使用guardian将该组与我们的Post模型相关联:

from django.db import models
from django.conf import settings
from django.urls import reverse
from guardian.shortcuts import assign_perm

class Post(models.Model):
    title = models.CharField(max_length=255)
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('post_detail', args=[str(self.id)])

class Writer(Group):

    class Meta:
        verbose_name = "Writer"
        verbose_name_plural = "Writers"

# 在Post模型类中添加如下代码
class Meta:
    permissions = (("can_view_post", "Can view post"),)

assign_perm("can_view_post", Writer.objects.get_or_create(name="nobody")[0])

3.现在,我们需要为我们的Writer组提供一个权限的表单:

# 在admin.py中的PostModelAdmin类中添加如下代码
from django.contrib.auth.models import Group
from django.forms import CheckboxSelectMultiple
from guardian.admin import GuardedModelAdmin

class GroupAdminForm(forms.ModelForm):
    class Meta:
        model = Group
        fields = '__all__'
        widgets = {
            'permissions': CheckboxSelectMultiple,
        }

    permissions = forms.MultipleChoiceField(
        choices=[],
        widget=CheckboxSelectMultiple,
        help_text=_('Permissions of the group')
    )

    def __init__(self, *args, **kwargs):
        super(GroupAdminForm, self).__init__(*args, **kwargs)

        choices = [(x.id, x.name) for x in Permission.objects.all().order_by('content_type__app_label', 'content_type__model', 'codename')]
        self.fields['permissions'].choices = choices

    def save(self, commit=True):
        group = super(GroupAdminForm, self).save(commit)

        if commit:
            perms = self.cleaned_data.get('permissions', [])
            permissions = Permission.objects.filter(pk__in=perms)
            group.permissions.set(permissions)

        return group

@admin.register(Writer)
class WriterAdmin(GuardedModelAdmin):
    form = GroupAdminForm

4.接下来,我们为GroupAdminForm类提供一个角色权限筛选器:

# 在admin.py中的GroupAdminForm类中添加如下代码
from django.contrib.auth.models import Permission

class PermissionFilter(SimpleListFilter):
    title = _('permissions')
    parameter_name = 'permissions'

    def lookups(self, request, model_admin):
        perms = Permission.objects.all().order_by('content_type__app_label', 'content_type__model', 'codename')
        choices = []
        for perm in perms:
            content_type = perm.content_type
            choice = (f'{content_type.app_label}.{content_type.model}.{perm.codename}', perm.name)
            if choice not in choices:
                choices.append(choice)
        return choices

    def queryset(self, request, queryset):
        if self.value():
            content_type, codename = self.value().rsplit('.', 1)
            queryset = queryset.filter(
                permissions__content_type__app_label=content_type,
                permissions__content_type__model=content_type,
                permissions__codename=codename
            )
        return queryset

5.最后,我们需要修改PostModelAdmin以允许我们通过角色权限过滤器过滤结果:

# 在admin.py中的PostModelAdmin类中添加如下代码
class PostModelAdmin(GuardedModelAdmin):
    list_display = ('title', 'body', 'created_at', 'updated_at')
    list_filter = ('author', PermissionFilter)

    def get_queryset(self, request):
        qs = super(PostModelAdmin, self).get_queryset(request)

        if request.user.has_perm('can_view_post'):
            writer = Writer.objects.filter(user=request.user).first()
            if writer:
                qs = qs.filter(author__in=writer.user_set.all())

        return qs

第二种示例:给文章添加文章标题级别的权限控制

这是一个稍微复杂一些的示例,我们将使用django-guardian为每个文章的所有标题添加行级别权限。这意味着每个给定用户只能查看其被授权的文章的一部分,而不能查看全文。例如,假设我们创建了一篇名为“ hello_world ”的文章,它的标题是“ Hello World ”,它是通过一个包含三个<h2>标记的HTML文本构成的:

<h2>Hello</h2>
<h2>World</h2>
<h2>Today</h2>

现在假设我们有两个用户,名为Alice和Bob,Alice有权查看"Hello",而Bob有权查看"World"和"Today"。在本例中,我们将使用将每篇文章标题拆分为单独的条目的机制来实现。

  1. 首先,我们需要在models.py中为标题创建一个单独的模型,并将其与文章关联:
from django.db import models
from django.conf import settings
from django.urls import reverse

class Post(models.Model):
    title = models.CharField(max_length=255)
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('post_detail', args=[str(self.id)])

class PostTitle(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='titles')
    title = models.CharField(max_length=255)

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "Post Title"
        verbose_name_plural = "Post Titles"

2.现在,我们需要为每个标题分配一个权限。假设文章标题post.titles.all()的结果为以下内容:

[<PostTitle: Hello>, <PostTitle: World>, <PostTitle: Today>]

我们将授予Alice访问"Hello"的权限和Bob访问注意任何一个标题的权限。为了将权限分配给各个用户,我们将使用以下代码:

from django.contrib.auth.models import User
from guardian.shortcuts import assign_perm

post = Post()
post.save()

titles = ['Hello', 'World', 'Today']
for title in titles:
    PostTitle.objects.create(post=post, title=title)

alice = User.objects.create(username='alice')
bob = User.objects.create(username='bob')

assign_perm('view_posttitle', alice, post.titles.all()[0])
for title in post.titles.all():
    assign_perm('view_posttitle', bob, title)

这将导致以下授权:

  • Alice可以看到"Hello",而Bob不能。
  • Bob可以查看所有标题,而Alice不能。

  • 现在,我们将修改PostTitleModelAdmin以隐藏每个区域的权限和添加我们的自定义列表视图:

from django.conf.urls import url
from django.contrib import admin
from django.urls import reverse
from django.utils.html import format_html
from guardian.admin import GuardedModelAdmin

from .models import Post, PostTitle

class PostTitleInline(admin.TabularInline):
    model = PostTitle
    extra = 1


@admin.register(Post)
class PostModelAdmin(GuardedModelAdmin):
    inlines = [PostTitleInline]
    list_display = ('title', 'created_at', 'updated_at')
    ordering = ['-created_at']

    def get_queryset(self, request):
        qs = super(PostModelAdmin, self).get_queryset(request)

        if request.user.has_perm('view_posttitle'):
            qs = qs.filter(titles__pk__in=request.user.get_objects_for_user('view_posttitle', klass=PostTitle).values_list('pk', flat=True))

        return qs


@admin.register(PostTitle)
class PostTitleModelAdmin(GuardedModelAdmin):
    list_display = ('post', 'title_link')
    list_filter = ('post',)
    search_fields = ('title',)
    ordering = ('post', 'id')

    def has_view_permission(self, request, obj=None):
        return False

    def get_urls(self):
        urls = super(PostTitleModelAdmin, self).get_urls()
        info = self.model._meta.app_label, self.model._meta.model_name
        my_urls = [
            url(r'^(?P<pk>\d+)/view/$', self.admin_site.admin_view(self.view_title), name='%s_%s_view' % info),
        ]
        return my_urls + urls

    def view_title(self, request, pk):
        title = self.get_object(request, pk)
        return admin.views.main.single_object_template_response(request, title, 'view_title.html')

    def title_link(self, obj):
        url = reverse('admin:%s_%s_view' % (self.model._meta.app_label, self.model._meta.model_name), args=[obj.pk])
        return format_html('<a href="{}">{}</a>', url, obj.title)
    title_link.admin_order_field = 'title'
    title_link.short_description = 'Title'
  1. 创建我们的自定义模板(view_title.html)以允许我们仅显示每个标题的一部分:
{% extends "admin/base_site.html" %}

{% block content_title %}
    {{ object.title|safe }}
{% endblock %}

{% block content %}
    {% for title in object.post.titles.all %}
        {% if title|has_perm:'view_posttitle' %}
            <h2>{{ title.title|safe }}</h2>
        {% endif %}
    {% endfor %}
{% endblock %}

现在,当我们单击每个标题时,我们将只能看到我们已授权的部分。

详细的攻略就是这样了,希望能对你有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:使用django-guardian实现django-admin的行级权限控制的方法 - Python技术站

(0)
上一篇 2023年5月16日
下一篇 2023年5月16日

相关文章

  • Django实战之用户认证(初始配置)

    下面是详细讲解“Django实战之用户认证(初始配置)”的完整攻略。 概述 在Django项目中,用户认证是必不可少的一步。Django提供了一套完善的用户认证系统,可以方便地实现用户的认证、注册、密码重置等功能。本文将介绍Django用户认证的初始配置过程。 准备工作 在开始之前,需要做以下准备工作: 安装Django:使用pip安装Django,可以输入…

    Django 2023年5月16日
    00
  • 详解基于django实现的webssh简单例子

    下面我将详细讲解如何实现基于Django实现的WebSSH简单例子,包含两条示例说明。 概述 WebSSH是基于WebSocket协议实现的网页SSH终端。本篇攻略将介绍如何用Django框架搭建一个WebSSH的简单示例,包括前端HTML和JS、后端Django的实现过程以及如何使用WebSocket模块。 示例1:创建Django项目 首先创建Djang…

    Django 2023年5月16日
    00
  • python ==> Django.view(登录,注册,个人页)

    Django-views 1.本篇博客主要是想实现一个:通过Django创建一个项目,目的: 三个界面,用户登录界面,用户注册界面,用户登录成功之后的个人页。本篇功能很简单,就是简单的时间页面的跳转,主要以练习为主。 首先:我们先把项目创建好,这里我创建的Django的项目名为:mysite_views,并且创建一个应用项目:app01.  两种创建方式: …

    Django 2023年4月11日
    00
  • Django ModelForm用法详解

    Django ModelForm是一种自动生成表单的工具,它是以模型为基础,在模型类上定义的表单。在使用Django ModelForm时,我们只需要指定模型类作为表单数据的基础,就可以自动地生成表单。下面是Django ModelForm用法的完整攻略。 创建ModelForm 首先,我们需要定义一个ModelForm。在创建ModelForm时,需要通过…

    Django 2023年3月12日
    00
  • Django 一对多序列化和反序列化

     首先看一下model.py这个文件 #学生的表 用多对多来关联另一个表class StudentModel(models.Model): username = models.CharField(max_length=43) password = models.CharField(max_length=43) course = models.ManyToMa…

    Django 2023年4月13日
    00
  • django+uwsgi+nginx部署(非常详细)

    django+uwsgi+nginx部署   1.介绍:   在网上看了很多教程,但自己部署了很久都没有成功,这篇博文记录自己所踩过得坑。   2.环境: 1 Ubuntu 16.04.1 LTS (GNU/Linux 4.4.0-130-generic x86_64)   3.下载uwsgi以及nginx 1 apt-get install uwsgi 2…

    Django 2023年4月11日
    00
  • [Django]用户权限学习系列之Permission权限基本操作指令

    若需建立py文件进行测试,则在文件开始加入以下代码即可 #coding:utf-8 import os os.environ.setdefault(“DJANGO_SETTINGS_MODULE”, “www.settings”) ”’ Django 版本大于等于1.7的时候,需要加上下面两句 import django django.setup() 否则…

    Django 2023年4月10日
    00
  • 利用django框架,手把手教你搭建数据可视化系统(一)

    如何使用django去构建数据可视化的 web,可视化的结果可以呈现在web上。 基础铺垫—MTV模型 Created with Raphaël 2.1.0 首先,要搞清楚我们去访问服务器,服务器返回信息的行为。 1)Request向服务器(Djangoweb)发起请求 点击页面,或查看网页信息,都是这个过程 2)服务器回应-Response 服务器抛出的可…

    Django 2023年4月12日
    00
合作推广
合作推广
分享本页
返回顶部