Django 用 user、group 和 permission 完成了权限机制,这个权限机制是将属于 model 的某个 permission 赋予 user 或 group,可以理解为全局的权限。
即如果用户 A 对数据模型(model)B 有可写权限,那么 A 能修改 model B 的所有实例(objects)。group 的权限也是如此,如果为 group C 赋予 model B 的可写权限,则隶属于 group C 的所有用户,都可以修改 model B 的所有实例。
1.Django的权限项
Django 用 permission 对象存储权限项,每个 model 默认都有三个 permission,即 add、change 和 delete。
我们在 Django 项目里创建一个叫 myApp 的 app,然后在 modles 添加一个 Book 模型:
from django.db import models
class Book(models.Model):
title = models.CharField(null=True, blank=True, max_length=200)
def __str__(self):
return self.title
打开 admin 可以在 user 的管理界面看到 Book 的三个 permission:
因为在定义好 Book 之后,Django 会自动创建相应的三个 permission:add_book
、change_book
和 delete_book
。
每个 permission 都是 django.contrib.auth.Permission
类型的实例,该类型包含三个字段 name
, codename
和 content_type
。
-
content_type
反应了 permission 属于哪个 model(如:Book) -
codename
是代码逻辑中检查权限时要用(如:'add_book') -
name
是 permission 的描述,将 permission 打印到屏幕或页面时默认显示的就是name(如:Can add book)
我们可以用 has_perm()
方法来检验某个用户是否用于某种权限:
from django.contrib.auth.models import User
u = User.objects.get(username='diego')
# has_perm 的 参数格式是: 'app_label.codename'
>>> u.has_perm('myApp.add_book')
True
>>> u.has_perm('myApp.change_book')
True
>>> u.has_perm('myApp.delete_book')
True
可见超级用户 diego 拥有对 Book 模型的所有对象进行 add、change、delete 操作的权限。
2.用户权限管理
我们再创建一个普通的用户 test_user,然后检测下它的权限:
u = User.objects.get(username='test_user')
>>> u.has_perm('myApp.add_book')
False
>>> u.has_perm('myApp.change_book')
False
>>> u.has_perm('myApp.delete_book')
False
新创建的普通用户并没有对 Book 对象 add、change、delete 的权限,现在我们来给用户 test_user 增加权限:
# 用户模型、权限模型
from django.contrib.auth.models import User, Permission
u = User.objects.get(username='test_user')
# 通过 codename 找到对应的权限
permission = Permission.objects.get(codename='change_book')
# 把权限赋予给该用户
u.user_permissions.add(permission)
>>> u.has_perm('myApp.change_book')
True
同理,还可以对用户权限进行其他操作:
# 设置权限
myuser.user_permissions = [permission_list]
# 设置权限
myuser.user_permissions.set([permission_list])
#增加权限
myuser.user_permissions.add(permission, permission, ...)
#删除权限
myuser.user_permissions.remove(permission, permission, ...)
#清空权限
myuser.user_permissions.clear()
# 注意:myuser 是一个用户对象,permission 是一个权限对象
3.自定义权限
Django 还允许自定义 permission,例如,我们可以为 Book 创建新的权限项:read_book, vote_book(参见豆瓣:读过、评分)等等。
现在我们为 Book 模型增加两项新的 permission,分别为 read_book 和 vote_book:
from django.db import models
class Book(models.Model):
title = models.CharField(null=True, blank=True, max_length=200)
def __str__(self):
return self.title
class Meta:
# 自定义的权限,两参数分别是权限的名字和权限的描述
permissions = (
("read_book", "Can Read Book"),
("vote_book", "Can Vote Book"),
)
再打开 admin 检查,可以看到刚才新增加的:
再做个测试:
u = User.objects.get(username='diego')
>>> u.has_perm('myApp.read_book')
True
>>> u.has_perm('myApp.vote_book')
True
超级管理员同样拥有新增加的 permission。
附注:
user.get_all_permissions()
方法列出用户的所有权限,返回值是 permission name 的 listuser.get_group_permissions()
方法列出用户所属 group 的权限,返回值是 permission name的 list
4.组别(Group)
用户组模型很简单,和 User 模型是多对多的关系。用户组顾名思义,就是对用户进行了分组。其作用在权限控制中就是可以批量的对用户的许可进行分配,而不用一个一个的按用户分配,节省维护的工作量。
将一个用户加入到一个 Group 中,该用户就拥有了该 Group 所分配的所有许可。例如,如果一个组 reader 有权限 read_book 和 vote_book。那么所有属于 reader 组的用户都会有这个权限。
下面我们创建一个新分组 reader,再把某用户加入到该组,再给该组添加上权限:
# 创建一个分组
Group.objects.create(name='reader')
# 获取某用户
u = User.objects.get(username='test_user')
# 获取某分组
g = Group.objects.get(name='reader')
# 把用户加入到分组中
u.groups.add(g)
# 获取某个权限
p= Permission.objects.get(codename='read_book')
# 把该权限加入到分组
g.permissions.add(p)
Group 还有其他操作:
# 把用户加入分组,group_list可以是一个或多个分组
u.groups = [group_list]
# 把用户加入某分组
u.groups.add(group, group, ...)
# 把某用户从某分组删除
u.groups.remove(group, group, ...)
# 该用户退出所以分组
u.groups.clear()
# 把权限加入到该分组
g.permissions.add(permission, permissions, ...)
# 从该分组删除某权限
g.permissions.remove(permission, permissions, ...)
# 清除该分组所以权限
g.permissions.clear()
5.permission_required 装饰器
使用了permission_required 装饰器之后,Django 会检查用户是否拥有指定的 permission,有相应 permission 的用户才能访问该页面:
from django.contrib.auth.decorators import permission_required
# 图书阅览页面
# 需要有权限 book.read_book 才访问该页面
# 否则跳转到 user_login 页
@ permission_required(perm='book.read_book', login_url="/user_login/")
def read_book(request, id):
context = {}
book = Book.objects.get(id=id)
context['book'] = book
return render(request, 'read_book.html', context)
6.Template 中的权限检查
Template 中使用全局变量 perms
存储当前用户的所有权限:
# 以下内容只对拥有 vote_book 权限的用户显示
{% if perms.myApp.vote_book %}
<button>打分</button>
{% else %}
<p>你不能打分</p>
{% endif %}