1. Permission概述
Permission是django自带的一个权限管理模块,它允许我们根据用户角色对view、model、object等进行权限控制,确定哪些用户可以进行哪些操作。在django中,Permission和认证系统Authentication是密切相关的,必须先进行认证,才能进行权限控制,否则django会抛出异常。
Permission在django中广泛应用于admin站点和restframework的APIView中,我们也可以在自己的项目中使用Permission进行权限控制。
2. Permission的实现
在django中,Permission的实现需要通过两个类来完成:
2.1 Permission
Permission类是django自定义权限的基础类,我们可以通过继承Permission类自定义我们需要的权限。Permission类有以下两个方法:
has_permission(self, request, view):该方法应返回一个bool值,确定用户是否有访问该view的权限。
has_object_permission(self, request, view, obj):该方法应返回一个bool值,确定用户是否有访问该obj的权限。
通常,我们会在has_permission方法中检查用户是否有访问该view的权限,如果有,返回True,否则返回False。在has_object_permission方法中,我们通常需要确定用户是否有访问该obj的权限,如果有,返回True,否则返回False。obj参数代表需要进行权限控制的model instance,如果我们没有需要进行obj级别的权限控制,则可以忽略该方法。
2.2 UserPassesTestMixin
UserPassesTestMixin是django提供的一个Mixin类,它重载了dispatch方法,在dispatch方法中进行了has_permission的调用。如果has_permission返回False,则用户被重定向到login页面进行认证。
from django.contrib.auth.mixins import UserPassesTestMixin
from django.urls import reverse_lazy
class MyView(UserPassesTestMixin):
def test_func(self):
return self.request.user.is_authenticated
def handle_no_permission(self):
return redirect(reverse_lazy('login'))
上面的例子中,MyView继承了UserPassesTestMixin类,当请求到达MyView时,test_func方法会被调用,该方法应返回一个bool值。如果返回False,则该请求会被重定向到handle_no_permission方法指定的URL页面进行处理。在上面的例子中,我们将被重定向到login页面。
3. Permission的应用
django自带认证系统Authentication,我们可以通过为用户设置不同的角色,来限制用户访问某些view或者obj。在django中,角色通常是通过Group来进行分配和管理,一个Group可以拥有多个Permission,而一个User可以属于多个Group。因此,我们可以通过为User添加或者删除Group,来改变User的角色和权限。
3.1 django admin站点中的Permission
在admin站点中,我们可以通过为User对象分配不同的Group,来限制User访问admin站点中的各个model和view。在admin.py文件中,我们可以通过以下代码设置admin站点的权限:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User, Group
from django.contrib.auth import get_permission_codename
class MyUserAdmin(UserAdmin):
def get_group_permissions(self, user_obj, obj=None):
"""
Return a queryset of permission strings that this user has through their
groups. This method should be used instead of
``user_obj.group_set.permissions()`` if looking to annotate extra data
onto the permissions. If you're just checking for permissions, use
``user_obj.group_set.permissions`` instead.
"""
permissions = super().get_group_permissions(user_obj, obj=obj)
return permissions.annotate(
name=F('codename'),
app_label=Value('auth'),
namespace=Value('admin'),
content_type=F('group__permissions__content_type'),
codename=F('group__permissions__codename'),
)
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.annotate(
name=F('username'),
app_label=Value('auth'),
namespace=Value('admin'),
content_type=Value(None, output_field=ContentType),
codename=get_permission_codename('view', User._meta),
)
admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)
通过上面的代码,我们可以在admin站点中为User对象分配Group,并通过Group控制User对象的权限。我们也可以自定义Permission,并将其分配给不同的Group。如果我们想在admin站点中自定义Permission,我们可以在admin.py文件中编写以下代码:
class MyPermission(permissions.BasePermission):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
return True
def __str__(self):
return 'my_permission'
admin.site.register(MyPermission)
在以上代码中,我们自定义了MyPermission,该Permission在has_permission和has_object_permission方法中都返回了True,即所有用户都有my_permission。我们将MyPermission注册到admin站点中,便可以在admin站点中为不同的Group分配该权限。
3.2 rest framework中的Permission
在rest framework中,我们同样可以使用Permission进行权限控制。在APIView中,我们可以通过permission_classes属性设置需要使用的Permission,例如:
class MyView(APIView):
permission_classes = (IsAdminUser,)
以上代码中,我们将IsAdminUser的实例赋值给permission_classes属性,该Permission将只允许admin用户访问该view。
我们也可以在自定义的视图中重载get_permission方法来进行权限控制:
class MyPermission(permissions.BasePermission):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
return True
class MyView(APIView):
def get_permission(self):
return (MyPermission(),)
在以上代码中,我们将MyPermission的实例作为get_permission方法的返回值,该Permission将允许所有用户访问该view。
4. Permission高级设置
在django中,我们可以使用model中的Meta类中的permissions属性定义Permission,使用方法如下:
class Book(models.Model):
name = models.CharField(max_length=128)
author = models.CharField(max_length=128)
class Meta:
permissions = [
('can_view', 'Can View Book'),
('can_edit', 'Can Edit Book'),
]
在以上代码中,我们为Book model定义了两个Permission:can_view和can_edit。Permission的第一个参数是权限编码,第二个参数是权限描述。
如果我们想使用Model中的Permission,在view中可以这样使用:
class MyView(APIView):
def get_queryset(self):
if self.request.user.has_perm('myapp.can_edit'):
return Book.objects.all()
else:
return Book.objects.filter(author=self.request.user.username)
在以上代码中,我们在get_queryset方法中使用了has_perm方法,该方法可用于检查用户是否有某个Permission。
5. 总结
Permission是django自带的一个权限管理工具,它可以用于访问控制、用户身份验证和参数验证等方面。因此,我们需要在django的开发中更加深入地学习和使用Permission,以便在对项目进行维护、升级和扩展时,能够更有效地实现自己的需求。