转自并修改:
https://django-guardian.readthedocs.io/en/latest/userguide/assign/
安装
pip install django-guardian
配置 setting.py
1、setting.py 将 guardian 加入到 INSTALLED_APPS
INSTALLED_APPS = (
# ... 
'guardian',
)
2、setting.py 加入到身份验证后端 AUTHENTICATION_BACKENDS
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', # 这是Django默认的
'guardian.backends.ObjectPermissionBackend', # 这是guardian的
)
 注意:将 django-guardian 配置进项目,当使用 migrate 命令时 auth_user 表里面会创建一个匿名用户的实例(名为 AnonymousUser )。guardian的匿名用户与Django的匿名用户不同。Django匿名用户在数据库中没有条目,但是Guardian 匿名用户有。

额外设置
GUARDIAN_RAISE_403:如果GUARDIAN_RAISE_403设置为True,guardian将会抛出 django.core.exceptions.PermissionDenied 异常,而不是返回一个空的 django.http.HttpResponseForbidden GUARDIAN_RENDER_403:如果GUARDIAN_RENDER_403设置为True,将会尝试渲染403响应,而不是返回空的django.http.HttpResponseForbidden。模板文件将通过GUARDIAN_TEMPLATE_403来设置。
ANONYMOUS_USER_NAME用来设置匿名用户的用户名,默认为 AnonymousUser。
GUARDIAN_GET_INIT_ANONYMOUS_USER:Guardian支持匿名用户的对象级权限,但是在我们的项目中,我们使用自定义用户模型,默认功能可能会失败。这可能导致guardian每次migrate之后尝试创建匿名用户的问题。将使用此设置指向的功能来获取要创建的对象。一旦获取,save方法将在该实例上被调用。默认值为guardian.ctypes.get_default_content_type
GUARDIAN_GET_CONTENT_TYPE:Guardian允许应用程序提供自定义函数以从对象和模型中检索内容类型。当类或类层次结构以ContentType非标准方式使用框架时,这是有用的。大多数应用程序不必更改此设置。
例如,当使用django-polymorphic适用于所有子模型的基本模型上的权限时,这是有用的。在这种情况下,自定义函数将返回ContentType多态模型的基类和ContentType非多态类的常规模型。
 默认为guardian.ctypes.get_default_content_type。
与Django admin集成
Django配有优秀和广泛使用的admin应用程序。它为Django应用程序提供基本的内容管理。具有访问管理面板的用户可以管理系统提供的用户,组,权限和其他数据。django-guardian 为Django的admin提供简单的对象许可管理集成。
设置和使用对象权限:
首先当然是设置和使用对象权限了,guardian 提供了一个简单的方法:
guardian.shortcuts.assign(perm, user_or_group, obj=None),这个方法接受3个参数:
- perm,这个参数是一个字符串,代表一个许可,格式必须为<app>.<perm_codename>或者<perm_codename>。但是如果第三个参数是None,则必须为<app>.<perm_codename>格式。因此建议还是统一使用<app>.<perm_codename>格式。注意app并不是app的全路径,而是最后一级的模块名。这一点和INSTALL_APP中的app全路径不同,如果你的app module不只一级的话,这地方一定要注意。
- user_or_group,这个参数是一个User或者Group类型的对象。
- obj,这个参数就是相关的对象了。该参数是可省略的,如果省略则赋予Model权限。
通过这个方法我们可以很方便通过传入一个 <app>.<perm_codename> 格式的字符串来给用户User或组Group赋予权限了。如果不传入第三个参数,则可以当作 User.user_permissions.add(permissioninstance)  的快捷方式。
 下面是赋予模型级别的权限:
from guardian.shortcuts import assign
user = User.objects.create(username='liuyong')
assign('app.view_task', user) # 赋予模型级别的权限
user.has_perm('app.view_task')
>>True注意,一旦赋予模型级的权限,那么所有该模型的对象级别的权限就都有了,所以应该先从对象级别进行设置,清空刚刚分配的权限然后再设置对象权限:
user = User.objects.get(username='liuyong')
user.user_permissions.clear() # 清空刚刚分配的权限
task = Task.objects.create(summary='Some job', content='') # 创建一个对象
assign('app.view_task', user, task) # 为用户user设置对象权限(task是一个对象,对应Task表格中的一行)
user = User.objects.get(username='liuyong')#刷新缓存
user.has_perm('app.view_task',task)
>>True
user.has_perm('app.view_task')#模型级别的权限还没有
>>False我们也可以通过设置 group 来使用户具有相应的权限:
>>> group = Group.objects.create(name='employees') # 创建group
>>> assign('change_task', group, task) # group组有task的权限
>>> user.has_perm('change_task', task)
False
>>> # user还不是employees组的成员,我们加入一下
>>> user.groups.add(group) ## user还不是employees组的成员,我们加入一下
>>> user.has_perm('change_task', task)
True 接下来是删除某个用户对某个对象的某种许可,我们需要使用 guardian.shortcuts 模块中的 remove_perm() 函数。这个函数的签名和 assign 相同,都是三个:
 guardian.shortcuts.remove_perm(perm,user_or_group=None, obj=None)
 样例代码:
>>> from guardian.shortcuts import remove_perm
>>> remove_perm('change_site', user, site) # 
>>> user = User.objects.get(username='joe') #刷新user对象缓存
>>> joe.has_perm('change_site', site) # user.has_perm('change_site', site)
False上面就是guadian的安装配置和基本使用方法,下面介绍在 Django 的 View 中所能使用的一些 helper 函数。
Guardian在View中的使用
除了 Django 的 user.has_perm 方法之外,guardian 提供了一些函数。
1、guardian.shortcuts.get_perms(user_or_group,obj)
该方法返回“user对象或者group对象”对obj对象所有的权限。这个行数接受两个参数,一个是user对象或者group对象,一个是相关的对象。
比如我们可以用:
'permcodename' in get_perms(group,obj) # 判断该组是否有这个权限,因为group没有has_perm方法。
2、guardian.shortcuts.get_objects_for_user(user, perms, klass=None, use_groups=True, any_perm=False)
该函数获得该用户下指定perm列表中的所有对象。
比如我要获得某一个用户,拥有编辑权限的所有帖子。
get_objects_for_user(user,'app.change_post')
>>所有可编辑的帖子
3、guadian.core.ObjectPermissionChecker(user_or_group)
该方法是一个用来判断权限的包装器,针对user和group提供权限相关的访问方法,主要有has_perm(perm,obj)和get_perms(obj)两个方法。并且提供缓存机制,在多次查找权限的时候,可以使用它。
>>> epser = User.objects.get(username='esper')
>>> site = Site.objects.get_current()
>>> from guardian.core import ObjectPermissionChecker
>>> checker = ObjectPermissionChecker(esper) # 我们也可以传入组group对象
>>> checker.has_perm('change_site', site)
True
>>> checker.has_perm('add_site', site) # 这次将不会产生数据库查询
False
>>> checker.get_perms(site)
[u'change_site']
4、使用view的decorator来减少代码
下面的代码,演示了通过decorator控制一个view函数的访问。我们要做到的是,只有拥有对name=foobars的Group对象拥有auth.change_group权限的用户,才能够执行这个view函数,否则返回的将是状态码为403的Response对象。
>>> user_joe = User.objects.get(username='user_joe')
>>> group_foobars = Group.objects.create(name='group_foobars')
>>> from guardian.decorators import permission_required_or_403
>>> from django.http import HttpResponse
>>> @permission_required_or_403('auth.change_group',
>>>     (Group, 'name', 'group_name'))
>>> def edit_group(request, group_name):
>>>     return HttpResponse('some form')
>>> from django.http import HttpRequest
>>> request = HttpRequest()
>>> request.user = user_joe
>>> edit_group(request, group_name='group_foobars')
<django.http.HttpResponseForbidden object at 0x102b43dd0>
>>> user_joe.groups.add(group_foobars)
>>> edit_group(request, group_name='group_foobars')
<django.http.HttpResponseForbidden object at 0x102b43e50>
>>> from guardian.shortcuts import assign
>>> assign('auth.change_group', user_joe, group_foobars)
<UserObjectPermission: group_foobars | user_joe | change_group>
>>> edit_group(request, group_name='group_foobars')
<django.http.HttpResponse object at 0x102b8c8d0>
# 这时,我们已经分配了权限,因此我们的view方法得以顺利访问了。
Guardian 在模版中的使用
 和Django一样,我们也需要在界面上进行权限控制以显示不同的界面。
 Guardian提供了标签:
 get_obj_perms
 需要加载guardian_tags标签库,在需要使用guardian标签的模版上面,将其引用近来:
 {% load guardian_tags %}
 标签格式为:
 {% get_obj_perms user/group for obj as "context_var" %}
 例子代码如下:
 {% get_obj_perms request.user for flatpage as "flatpage_perms" %}
 {% if "delete_flatpage" in flatpage_perms %}
     <a href="/pages/delete?target={{ flatpage.url }}">Remove page</a>
 {% endif %}
实例项目
准备模型和自定义权限
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Task(models.Model):
    summary = models.CharField(max_length=32)
    content = models.TextField()
    reported_by = models.ForeignKey(User)
    created_at = models.DateTimeField(auto_now_add=True)
    class Meta:
        permissions = (
            ('view_task', 'View task'),
        )说明:
permissions是我们自定义的权限,当我们调用migrate命令的时候,view_task将会被添加到默认的权限集合中。
默认情况下Django为每个模型注册3个权限
* add_模型名
* change_模型名
* delete_模型名
By default, Django adds 4 permissions for each registered model:
- add_modelname
- change_modelname
- delete_modelname
- view_modelname
分配对象权限:
使用 guardian.shortcuts.assign_perm() 方法可以为用户/组分配对象权限
为用户分配权限
>>> from django.contrib.auth.models import User
>>> from todo.models import Task
>>> from guardian.shortcuts import assign_perm
>>> boss = User.objects.create(username="Big Boss") # 创建用户boss
>>> joe = User.objects.create(username="joe") # 创建用户joe
>>> task = Task.objects.create(summary="Some job", content="", reported_by=boss) # 创建Task对象
>>> joe.has_perm('view_task', task) # 默认用户对这个对象没有权限
False
>>> assign_perm('view_task', joe, task) # 为用户joe分配权限
<UserObjectPermission: UserObjectPermission object>
>>> joe.has_perm('view_task', task)
True为用户组分配权限
>>> from django.contrib.auth.models import Group
>>> group = Group.objects.create(name="employees") # 创建group employees
>>> assign_perm("change_task", group, task)
    <GroupObjectPermission: GroupObjectPermission object>
>>> jack = User.objects.create(username="jack") # 创建用户jack
>>> jack.has_perm('change_task', task)
    False
>>> jack.groups.add(group) #  jack user还不是employees组的成员,我们加入一下
>>> jack.has_perm('change_task', task)
    True
检查对象权限
标准方式:使用用户实例的 has_perm 来检查是否有某种权限。
在视图中使用
get_perms
>>> from guardian.shortcuts import get_perms
>>> 'change_task' in get_perms(joe, task)
False
>>> 'change_task' in get_perms(jack, task)
True建议尽量使用标准 has_perm 方法。但是对于Group实例,它不是那么容易,get_perms 解决这个问题很方便,因为它接受 User和Group实例。如果我们需要做更多的工作,我们可以使用ObjectPermissionChecker 这个低级类。
 也可以使用 get_user_perms 获得直接分配权限给用户(而不是从它的超级用户权限或组成员资格继承的权限)。同样的,get_group_perms仅返回其是通过用户组的权限。
get_objects_for_user
有时候我们需要根据特定的用户,对象的类型和提供的权限来获取对象列表,例如
>>> from guardian.shortcuts import get_objects_for_user
>>> get_objects_for_user('view_task', joe)
<QuerySet [<Task: Task object>]
>>> get_objects_for_user(jack, 'todo.change_task')
<QuerySet [<Task: Task object>]>
>>> get_objects_for_user(jack, 'todo.view_task')
<QuerySet []>ObjectPermissionChecker
guardian.core.ObjectPermissionChecker 用于检查特定对象的用户/组的权限。因为他缓存结果,因此我们可以在多次检查权限的代码的一部分中使用
>>> from guardian.core import ObjectPermissionChecker
>>> cheker = ObjectPermissionChecker(joe)
>>> checker = ObjectPermissionChecker(joe)
>>> checker.has_perm('view_task', task)
True
>>> checker.has_perm('change_task', task)
False
使用装饰器
标准permission_required装饰器不允许检查对象权限。django-guardian随附两个装饰器,这可能有助于简单的对象权限检查,但请记住,在装饰视图被调用之前,这些装饰器会触发数据库——这意味着如果在视图中进行类似的查找,那么最可能的一个(或更多,取决于查找)会发生额外的数据库查询。
在模板中使用
django-guardian附带特殊模板标签guardian.templatetags.guardian_tags.get_obj_perms(),可以存储给定用户/组和实例对的对象权限。为了使用它,我们需要在模板中放置以下内容:
{% load  guardian_tags  %}guardian.templatetags.guardian_tags.get_obj_perms(parser, token)返回给定用户或者组和对象(Model实例)的权限列表。
调用格式为
{% get_obj_perms user/group for obj as "context_var" %}移除对象权限
删除对象权限和分配一样简单,我们使用 guardian.shortcuts.remove_perm() 来移除权限
>>> remove_perm("veiw_task",joe, task)
    (0, {'guardian.UserObjectPermission': 0})
>>> joe.has_perm("view_task", task)
    True
>>> remove_perm("view_task",joe, task)
    (1, {'guardian.UserObjectPermission': 1})
>>> joe.has_perm("view_task", task)
    False
孤儿对象许可(Orphaned object permissions)
所谓孤儿许可,就是没用的许可,大多数情况下,可能没事,但一旦发生,后果有可能非常严重。
 
Guardian用来纪录某用户对某个模型对象有某个权限的纪录时是使用UserObjectPermission和GroupObjectPermission对象纪录的。其中对于object的引用是contenttype对象(标示是那个模型类)和pk主键,对于用户则是对User表的外键引用。
 
比方说,有一个对象A。我们通过权限设置,设定joe用户对该对象有着编辑权限。忽然有一天,用户joe被删除了。可想而知,我们分配而产生的UserObjectPermission对象仍然在数据库里面,记录着:joe
 
有对A的编辑权限。又有一天,一个用户注册了一个用户,用户username为joe。因为之前的那个纪录,joe用户拥有对A的编辑权限。而此joe非彼joe,我们犯了一个大错误!
 
再比如说,当我们删除了某一个对象的时候,而这个对象的某种权限已经被赋予给某个用户,那么这个权限的纪录也就失效了。如果什么时候和曾经删除过的对象是同一个模型类,而且主键和以前的那个相同,那么用户也就有可能对其本不应该拥有权限的对象有了权限。呵呵,说起来有点绕,但是应该很容易理解。
 因此,当删除User和相关Object时,一定要删除其相关的所有UserObjectPermission和GroupObjectPermission对象。要解决这个问题有三种,一是显式编码,二是通过其提供的自定义django命令:
 $ python manage.py clean_orphan_obj_perms
 Removed 11 object permission entries with no targets
 还有一个是定期调用guardian.utils.clean_orphan_obj_perms(),该函数会返回删除的对象数目。也可使用celery定期调度任务。
 但是自定义命令和定期调度都不是生产环境的解决办法。还是需要手动编码实现,最优雅的方式还是加上post_delete signal给User或Object对象。如下:
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.db.models.signals import pre_delete
from guardian.models import UserObjectPermission
from guardian.models import GroupObjectPermission
from school.models import StudyGroup
def remove_obj_perms_connected_with_user(sender, instance, **kwargs):
    filters = Q(content_type=ContentType.objects.get_for_model(instance),
        object_pk=instance.pk)
    UserObjectPermission.objects.filter(filters).delete()
    GroupObjectPermission.objects.filter(filters).delete()
    pre_delete.connect(remove_obj_perms_connected_with_user, sender=StudyGroup)
https://django-guardian.readthedocs.io/en/latest/
========================================
========================================
========================================
REF
https://blog.51cto.com/u_6186189/6950636
    
    
 
 
                     
            
        













 
                    

 
                 
                    