下面是使用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"。在本例中,我们将使用将每篇文章标题拆分为单独的条目的机制来实现。
- 首先,我们需要在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'
- 创建我们的自定义模板(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技术站