项目描述

Rules是一个很小但功能强大的应用程序,为Django提供对象级权限,而不须要数据库。它的核心是构建基于规则的系统的通用框架,相似于决策树。它还能够在其余上下文和框架中用做独立的库。html

https://travis-ci.org/dfunckt/django-rules.svg?branch=master  https://coveralls.io/repos/dfunckt/django-rules/badge.svg  https://img.shields.io/pypi/v/rules.svg  https://img.shields.io/pypi/pyversions/rules.svg

特色

rules has got you covered. rules is:python

  • 文档、测试、可靠且易于使用。.
  • 灵活. 经过装饰可调用对象来构建复杂的谓词图 . Predicates 能够是任何类型的callable对象 – 简单函数、lambda、方法、可调用类对象、部分函数、修饰函数
  • A good Django citizen. 与Django视图、模板和管理员无缝集成,以测试对象级权限。.
  • 高效和聪明。没必要为了弄清楚 “约翰是否真的写了那本书” 而乱弄数据库。
  • 简单。深刻了解代码。你须要10分钟来弄清楚它是如何工做的
  • 强大。规则具备高级功能,例如调用上下文和存储任意数据、跳过特定条件下的谓词评估、记录评估的谓词等等!

安装需求

rules须要python 2.7/3.4或更高版本。它能够选择与Django集成,在这种状况下须要Django 1.11或更高版本。.git

Note: At any given moment in time, rules will maintain support for all currently supported Django versions, while dropping support for those versions that reached end-of-life in minor releases. See the Supported Versions section on Django Project website for the current state and timeline.github

从1.x升级

  • 支持Python 2.6 / 3.3, 不支持对Django==1.11以前版本的升级.
  • Predicate类的SkipPredicate 异常和 skip() 方法, (曾用于表示谓词是否跳过) 已被移除。 你能够从谓词中返回None来实现.
  • 替换rule的谓词的APIs被重命名且行为被更改. replace_rule 和 replace_perm 方法和 RuleSet的replace_rule方法已分别被从新命名为 set_rule,set_perm 和RuleSet.set_perm. 若是给定name的rule 不存在旧行为是抛出 KeyError 异常. 自2.0版以来,这一点已经改变,您能够安全地使用set_*来设置rule的谓词,而无需确保规则首先存在。

怎样安装

使用pip:web

$ pip install rules

手动安装:数据库

$ git clone https://github.com/dfunckt/django-rules.git
$ cd django-rules
$ python setup.py install

执行测试:django

$ ./runtests.sh

你可能须要阅读 Best practices 以得到有关如何使用 rules的通常性建议。编程

在Django中配置rules

INSTALLED_APPS = ( # ... 'rules', )
AUTHENTICATION_BACKENDS = ( 'rules.permissions.ObjectPermissionBackend', 'django.contrib.auth.backends.ModelBackend', ) 

使用 Rules

rules基于这样的思想: 你维护一个RuleSet类对象(相似dict),至关于一个映射表,由字符串标识到Predicate类对象的映射后端

建立predicates

暂时忽略 rule sets,先定义一个 Predicate对象. 最简单的方法是经过 @predicate 装饰器:api

>>> @rules.predicate >>> def is_book_author(user, book): ... return book.author == user ... >>> is_book_author <Predicate:is_book_author object at 0x10eeaa490> 

若是书的做者是给定的user,This predicate 将返回 True ,不然返回 False.

Predicates 能被建立 经过 接收任意数量的位置参数的可调用对象:

  • fn(obj, target)
  • fn(obj)
  • fn()

这是它们的通常形式。若是从Django的受权角度来看,等效签名为

  • fn(user, obj)
  • fn(user)
  • fn()

Predicates能够对给定的参数执行任何操做,但若是它们检查的条件为真,则必须始终返回真,不然返回假。规则附带了几个预约义的谓词,您稍后可能会在 API Reference,中了解到这些谓词,这些谓词在处理authorization in Django.时很是有用。

设置 rules

假设咱们想让做者编辑或删除他们的书,而不是其余做者写的书。因此,本质上,决定一个做者是否能够编辑或删除一本给定的书的是他们是不是做者。

在rules中,这些需求被当作rules。一个rule是惟一标识符(例如“can edit”)到predicate的映射。许多规则被组合成一个rule set。rules有两个预约义的rule set::

  • 其中一个默认的rule set 保存共享的 rules. rules.add_rule('name',  is_xxx)
  • 另一个rule set保存做为权限在Django上下文中生效的rules  rules.add_perm(...)

So, 让咱们定义第一组rules, 添加他们到共享的rule set. 咱们可使用以前定义过的 is_book_author谓词对象

>>> rules.add_rule('can_edit_book', is_book_author) >>> rules.add_rule('can_delete_book', is_book_author) 

假设咱们有一些数据,咱们如今能够测试咱们的规则:

>>> from django.contrib.auth.models import User >>> from books.models import Book >>> book1 = Book.objects.get(isbn='978-1-4302-1936-1') >>> book1.author <User: Tom> >>> author_tom = User.objects.get(username='Tom') >>> rules.test_rule('can_edit_book', author_tom, book1) True >>> rules.test_rule('can_delete_book', author_tom, book1) True 

Nice… but not awesome(棒).

组合predicates

Predicates自己并无那么有用——比任何其余函数都没那么有用。然而,Predicates可使用二元运算符组合以建立更复杂的谓词。谓词支持如下运算符:

  • P1 & P2: 且,若是P1,P2都为True则返回值为True的新predicate,不然返回 False. 若是 P1 为 False, P2 不会进行判断.
  • P1 | P2: 或,若是P1,P2有一个为True,则返回值为True的新 predicate , 不然返回False. 若是P1 为True, P2不会进行判断.
  • P1 ^ P2:异或,若是P1,P2一个True一个False,则返回值为True的新 predicate, 不然返回False.
  • ~P: 非,返回与原predicate相反结果的predicate.

 

假设容许用户编辑一本给定的书的要求是他们要么是书的做者,要么是“编辑”组的成员。容许用户删除一本书仍然应该由用户是不是书的做者来决定。

使用易于实现的规则。咱们必须定义另外一个谓词,若是给定的用户是“editors”组的成员,则返回true,不然返回false。内置的is_group_member 工厂将派上用场:

>>> is_editor = rules.is_group_member('editors') # 是否是某个角色 >>> is_editor <Predicate:is_group_member:editors object at 0x10eee1350> 

咱们能够将它与 is_book_author谓词进行合并,来建立一个新谓词来检查他们其中之一是否成立:

>>> is_book_author_or_editor = is_book_author | is_editor >>> is_book_author_or_editor <Predicate:(is_book_author | is_group_member:editors) object at 0x10eee1390> 

咱们如今能够更新 can_edit_book 规则:

>>> rules.set_rule('can_edit_book', is_book_author_or_editor) >>> rules.test_rule('can_edit_book', adrian, guidetodjango) True >>> rules.test_rule('can_delete_book', adrian, guidetodjango) True 

 让咱们测试一下,若是换其余用户会怎么样:

>>> martin = User.objects.get(username='martin') >>> list(martin.groups.values_list('name', flat=True)) ['editors'] >>> rules.test_rule('can_edit_book', martin, guidetodjango) True >>> rules.test_rule('can_delete_book', martin, guidetodjango) False 

很棒.

到目前为止,咱们只使用了底层的通用框架来定义和测试规则。这一层根本不特定于Django;它能够在任何上下文中使用。实际上,整个应用程序中没有任何与django相关的导入(rules.templateTags模块除外)。可是,规则能够与Django紧密集成以提供受权。So far, we’ve only used the underlying, generic framework for defining and testing rules. This layer is not at all specific to Django; it may be used in any context. There’s actually no import of anything Django-related in the whole app (except in the rules.templatetags module). rules however can integrate tightly with Django to provide authorization.

Using Rules with Django

rules能在Django中提供对象级别的权限控制,它附带了一个受权后端和几个用于模板中模板tags。

rules is able to provide object-level permissions in Django. It comes with an authorization backend and a couple template tags for use in your templates.

Permissions

在rules中,permissions是特定类型的rules,你仍可经过建立或合并谓词来定义他们,那些规则必须添加到权限指定的rule set以便rules认证后端能提取到

In rules, permissions are a specialised type of rules. You still define rules by creating and combining predicates. These rules however, must be added to a permissions-specific rule set that comes with rules so that they can be picked up by the rules authorization backend.

Creating permissions

Django中命名权限的约定是app_label.action_object,咱们但愿遵照这个约定。让咱们为书籍添加规则。更改书籍和书籍。删除书籍权限:

The convention for naming permissions in Django is app_label.action_object, and we like to adhere to that. Let’s add rules for the books.change_book and books.delete_book permissions:

>>> rules.add_perm('books.change_book', is_book_author | is_editor) >>> rules.add_perm('books.delete_book', is_book_author) 

看到API的区别了吗?add_perm 将添加到permissions-specific rule set,而add_rule 将添加到 default shared rule set。可是,重要的是要知道这两个规则集是分开的,这意味着在其中一个添加的rule不会在另外一个里可用。

See the difference in the API? add_perm adds to a permissions-specific rule set, whereas add_rule adds to a default shared rule set. It’s important to know however, that these two rule sets are separate, meaning that adding a rule in one does not make it available to the other.

Checking for permission

让咱们检查一下adrian 用户对guidetodjango 书籍的修改权限

Let’s go ahead and check whether adrian has change permission to the guidetodjango book:

>>> adrian.has_perm('books.change_book', guidetodjango) False 

当您调用User.has_perm方法时,django会询问每一个在settings.authentication-backends里的认证后端,用户是否具备对象的给定权限。当查询对象权限时,Django的默认认证后端始终返回false。rules带有一个认证后端,它能够经过查看 permissions-specific rule set来提供对象级权限。

让咱们在设置中添加rules认证后端:

When you call the User.has_perm method, Django asks each backend insettings.AUTHENTICATION_BACKENDS whether a user has the given permission for the object. When queried for object permissions, Django’s default authentication backend always returns Falserules comes with an authorization backend, that is able to provide object-level permissions by looking into the permissions-specific rule set.

Let’s add the rules authorization backend in settings:

AUTHENTICATION_BACKENDS = ( 'rules.permissions.ObjectPermissionBackend', 'django.contrib.auth.backends.ModelBackend', )

Now, checking again gives adrian the required permissions:

如今再次检查adrian 的对象权限

>>> adrian.has_perm('books.change_book', guidetodjango) True >>> adrian.has_perm('books.delete_book', guidetodjango) True >>> martin.has_perm('books.change_book', guidetodjango) True >>> martin.has_perm('books.delete_book', guidetodjango) False 

Permissions in models

注意:本节中描述的特性仅适用于Python3+。

NOTE: The features described in this section work on Python 3+ only.

对于一个model有一组权限是很常见的,好比Django用它的默认模型权限(如添加、更改等)提供的权限。当使用rules做为权限检查后端时,可使用新的meta选项以相似的方式声明任何model的对象级权限。

It is common to have a set of permissions for a model, like what Django offers with its default model permissions (such as addchange etc.). When using rules as the permission checking backend, you can declare object-level permissions for any model in a similar way, using a new Meta option.

首先,您须要将model的基类和元类切换到rules.contrib.models中提供的稍微扩展的版本。有几个classes和mixins可使用,这取决于您是否已经为您的模型使用了自定义的基和/或元类。扩展很是小,除了使其注册权限以外,不会以任何方式影响model的行为。

First, you need to switch your model’s base and metaclass to the slightly extended versions provided in rules.contrib.models. There are several classes and mixins you can use, depending on whether you’re already using a custom base and/or metaclass for your models or not. The extensions are very slim and don’t affect the models’ behavior in any way other than making it register permissions.

若是您正使用django.db.models.model做为您的model的基类,只需切换到RulesModel,就能够很好地使用了。

若是您已经有了一个向model添加公共功能的自定义基类,请将RulesModelMixin添加到它继承的类中,并将RulesModelBase设置为它的元类,以下所示:

  • If you’re using the stock django.db.models.Model as base for your models, simply switch over to RulesModel and you’re good to go.

  • If you already have a custom base class adding common functionality to your models, add RulesModelMixin to the classes it inherits from and set RulesModelBase as its metaclass, like so:

    from django.db.models import Model
    from rules.contrib.models import RulesModelBase, RulesModelMixin
    
    class MyModel(RulesModelMixin, Model, metaclass=RulesModelBase):
        ...
    
  • If you’re using a custom metaclass for your models, you’ll already know how to make it inherit from RulesModelBaseMixin yourself.

Then, create your models like so, assuming you’re using RulesModel as base directly:

若是您正在为model使用自定义元类,那么您就已经知道如何让它从RulesModelBaseMixin继承。

而后,像这样建立模型,假设您直接使用RulesModel做为基类:

import rules
from rules.contrib.models import RulesModel

class Book(RulesModel):
    class Meta:
        rules_permissions = {
            "add": rules.is_staff,
            "read": rules.is_authenticated,
        }

This would be equivalent to the following calls:

这至关于如下调用:

rules.add_perm("app_label.add_book", rules.is_staff)
rules.add_perm("app_label.read_book", rules.is_authenticated)

RulesModelMixin中有一些方法能够覆盖,以便自定义如何注册模型的权限。若是须要,请参阅文档化的源代码以了解详细信息。

特别感兴趣的是RulesModelMixin的get_perm 类方法,它能够用于将权限类型转换为相应的完整权限名称。若是须要以编程方式查询给定模型的某种类型的权限,这很方便:

There are methods in RulesModelMixin that you can overwrite in order to customize how a model’s permissions are registered. See the documented source code for details if you need this.

Of special interest is the get_perm classmethod of RulesModelMixin, which can be used to convert a permission type to the corresponding full permission name. If you need to query for some type of permission on a given model programmatically, this is handy:

if user.has_perm(Book.get_perm("read")):
    ...

Permissions in views

rules附带了一组装饰器来帮助您在视图中强制受权。

rules comes with a set of view decorators to help you enforce authorization in your views.

Using the function-based view decorator

对于基于函数的视图,可使用permission_required装饰器:

For function-based views you can use the permission_required decorator:

from django.shortcuts import get_object_or_404 from rules.contrib.views import permission_required from posts.models import Post def get_post_by_pk(request, post_id): return get_object_or_404(Post, pk=post_id) @permission_required('posts.change_post', fn=get_post_by_pk) def post_update(request, post_id): # ... 

用法是直截了当的,可是在上面的示例中有一件事是突出的,这就是get_post_by_pk 函数。此函数给定当前请求和传递给视图的全部参数,负责获取和返回要检查权限的对象,POST对象中的主键等于给定的post_id。这个特定的用例是很是常见的,因此为了节省一些输入,rules附带了一个通用的助手函数,您可使用它来声明性地完成这项工做。下面的例子至关于上面的例子:

Usage is straight-forward, but there’s one thing in the example above that stands out and this is the get_post_by_pk function. This function, given the current request and all arguments passed to the view, is responsible for fetching and returning the object to check permissions against – i.e. the Post instance with PK equal to the given post_id in the example. This specific use-case is quite common so, to save you some typing, rules comes with a generic helper function that you can use to do this declaratively. The example below is equivalent to the one above:

from rules.contrib.views import permission_required, objectgetter from posts.models import Post @permission_required('posts.change_post', fn=objectgetter(Post, 'post_id')) def post_update(request, post_id): # ... 

有关decorator和helper函数的详细信息,请参阅rules.contrib.views模块。

For more information on the decorator and helper function, refer to the rules.contrib.views module.

Using the class-based view mixin

Django包含一组访问混合函数,您能够在基于类的视图中使用它们来强制受权。rules 扩展了这个框架,经过mixin提供对象级权限,PermissionRequiredMixin。

下面的示例将针对视图的get_object 方法返回的实例自动测试权限:

Django includes a set of access mixins that you can use in your class-based views to enforce authorization. rules extends this framework to provide object-level permissions via a mixin, PermissionRequiredMixin.

The following example will automatically test for permission against the instance returned by the view’s get_object method:

from django.views.generic.edit import UpdateView from rules.contrib.views import PermissionRequiredMixin from posts.models import Post class PostUpdate(PermissionRequiredMixin, UpdateView): model = Post permission_required = 'posts.change_post' 

您能够经过覆盖get_object 或get_permission_object来自定义对象。

有关更多信息,请参阅django文档和rules.contrib.views模块。

You can customise the object either by overriding get_object or get_permission_object.

For more information refer to the Django documentation and the rules.contrib.views module.

Checking permission automatically based on view type

若是您使用rules.contrib.models提供的机制来注册模型的权限(如Permissions in models中所述),那么对于基于类的视图,还有另外一种方便的混合方法。

rules.contrib.views.AutoPermissionRequiredMixin能够识别使用的视图类型,并自动检查相应的权限。

若是应用标签为"posts",则此示例视图将在不进行任何进一步配置的状况下自动检查 "posts.change_post"权限:

If you use the mechanisms provided by rules.contrib.models to register permissions for your models as described in Permissions in models, there’s another convenient mixin for class-based views available for you.

rules.contrib.views.AutoPermissionRequiredMixin can recognize the type of view it’s used with and check for the corresponding permission automatically.

This example view would, without any further configuration, automatically check for the "posts.change_post" permission, given that the app label is "posts":

from django.views.generic import UpdateView
from rules.contrib.views import AutoPermissionRequiredMixin
from posts.models import Post

class UpdatePostView(AutoPermissionRequiredMixin, UpdateView):
    model = Post

默认状况下,django.views.generic中的通用CRUD视图映射到本机django权限类型(add、change、delete和view)。可是,当子类化autopermissionRequiredMixin时,能够扩展、更改或替换预约义的映射。有关如何正确执行此操做的详细信息,请参阅彻底文档化的源代码。

By default, the generic CRUD views from django.views.generic are mapped to the native Django permission types (addchangedelete and view). However, the pre-defined mappings can be extended, changed or replaced altogether when subclassing AutoPermissionRequiredMixin. See the fully documented source code for details on how to do that properly.

Permissions and rules in templates

规则带有两个模板标记,容许您测试模板中的rules和permissions。

rules comes with two template tags to allow you to test for rules and permissions in templates.

Add rules to your INSTALLED_APPS:

INSTALLED_APPS = ( # ... 'rules', ) 

Then, in your template:

{% load rules %}

{% has_perm 'books.change_book' author book as can_edit_book %}
{% if can_edit_book %}
    ...
{% endif %}

{% test_rule 'has_super_feature' user as has_super_feature %}
{% if has_super_feature %}
    ...
{% endif %}

Permissions in the Admin

若是您在Django中设置了要与permissions一块儿使用的rules,使用rules 在Admin中认证add/change/delete。Admin要求四种不一样的权限:

If you’ve setup rules to be used with permissions in Django, you’re almost set to also use rules to authorize any add/change/delete actions in the Admin. The Admin asks for four different permissions, depending on action:

  • <app_label>.add_<modelname>
  • <app_label>.view_<modelname>
  • <app_label>.change_<modelname>
  • <app_label>.delete_<modelname>
  • <app_label>

注意:view_permission在Django v2.1中是新的。

前四个很明显。第五个是在管理员的“仪表板”中显示应用程序所需的权限。如下是咱们虚构的 books 应用程序的一些规则示例:

Note: view permission is new in Django v2.1.

The first four are obvious. The fifth is the required permission for an app to be displayed in the Admin’s “dashboard”. Here’s some rules for our imaginary books app as an example:

>>> rules.add_perm('books', rules.always_allow) >>> rules.add_perm('books.add_book', is_staff) >>> rules.add_perm('books.view_book', is_staff | has_secret_access_code) >>> rules.add_perm('books.change_book', is_staff) >>> rules.add_perm('books.delete_book', is_staff) 

django-admin不支持对象权限,从这个意义上说,它永远不会请求对对象执行操做的权限,只容许用户对模型的(任何)实例执行操做。

若是要告诉Django用户是否对特定对象具备权限,则必须重写模型的ModelAdmin的如下方法:

Django Admin does not support object-permissions, in the sense that it will never ask for permission to perform an action on an object, only whether a user is allowed to act on (any) instances of a model.

If you’d like to tell Django whether a user has permissions on a specific object, you’d have to override the following methods of a model’s ModelAdmin:

  • has_view_permission(user, obj=None)
  • has_change_permission(user, obj=None)
  • has_delete_permission(user, obj=None)

rules带有一个自定义的ModelAdmin 子类rules.contrib.admin.ObjectPermissionsModelAdmin,它重写这些方法以将编辑的模型实例传递到受权后端,从而在管理中启用每一个对象的权限:

rules comes with a custom ModelAdmin subclass,rules.contrib.admin.ObjectPermissionsModelAdmin, that overrides these methods to pass on the edited model instance to the authorization backends, thus enabling permissions per object in the Admin:

# books/admin.py
from django.contrib import admin from rules.contrib.admin import ObjectPermissionsModelAdmin from .models import Book class BookAdmin(ObjectPermissionsModelAdmin): pass admin.site.register(Book, BookAdmin) 

Now this allows you to specify permissions like this:

>>> rules.add_perm('books', rules.always_allow) >>> rules.add_perm('books.add_book', has_author_profile) >>> rules.add_perm('books.change_book', is_book_author_or_editor) >>> rules.add_perm('books.delete_book', is_book_author) 

为了保持向后兼容性,Django将请求查看或更改权限。为了得到最大的灵活性,规则的行为略有不一样:若是而且仅当视图权限不存在规则时,规则才会请求更改权限。

To preserve backwards compatibility, Django will ask for either view or change permission. For maximum flexibility, rules behaves subtly different: rules will ask for the change permission if and only if no rule exists for the view permission.

Permissions in Django Rest Framework

Similar to rules.contrib.views.autopermission requiredmixin,there is arules.contrib.rest=uframework.autopermissions viewsetmixin for viewsets in Django rest framework.不一样之处在于,这并非从视野类型得到的许可,而是从API行动(创做、检索等)中得到的许可,而API行动力求获得许可。固然,它要求你使用规则中的混合物。contrib.modelswhen declaring models the API should operate on.

这是一个彻底自动化的许可证检查的可能的模型:

Similar to rules.contrib.views.AutoPermissionRequiredMixin, there is arules.contrib.rest_framework.AutoPermissionViewSetMixin for viewsets in Django Rest Framework. The difference is that it doesn’t derive permission from the type of view but from the API action (createretrieveetc.) that’s tried to be performed. Of course, it requires you to use the mixins from rules.contrib.modelswhen declaring models the API should operate on.

Here is a possible ModelViewSet for the Post model with fully automated CRUD permission checking:

from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from rules.contrib.rest_framework import AutoPermissionViewSetMixin
from posts.models import Post

class PostSerializer(ModelSerializer):
    class Meta:
        model = Post
        fields = "__all__"

class PostViewSet(AutoPermissionViewSetMixin, ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

默认状况下,modelviewset的CRUD操做映射到本机django权限类型(添加、更改、删除和查看)。列表操做未启用权限检查。可是,在使用(或子类化)autopermissionviewsetmixin时,能够彻底扩展、更改或替换预约义的映射。而后也能够映射经过@action decorator定义的自定义API操做。有关如何正确自定义默认行为的详细信息,请参阅完整文档化的源代码。

By default, the CRUD actions of ModelViewSet are mapped to the native Django permission types (addchangedelete and view). The list action has no permission checking enabled. However, the pre-defined mappings can be extended, changed or replaced altogether when using (or subclassing)AutoPermissionViewSetMixin. Custom API actions defined via the @action decorator may then be mapped as well. See the fully documented source code for details on how to properly customize the default behaviour.

Advanced features

Custom rule sets

You may create as many rule sets as you need:

>>> features = rules.RuleSet() 

And manipulate them by adding, removing, querying and testing rules:

>>> features.rule_exists('has_super_feature') False >>> is_special_user = rules.is_group_member('special') >>> features.add_rule('has_super_feature', is_special_user) >>> 'has_super_feature' in features True >>> features['has_super_feature'] <Predicate:is_group_member:special object at 0x10eeaa500> >>> features.test_rule('has_super_feature', adrian) True >>> features.remove_rule('has_super_feature') 

Note however that custom rule sets are not available in Django templates – you need to provide integration yourself.

Invocation context

新上下文是做为调用predicate.test()的结果建立的,而且仅在调用期间有效。上下文是一个简单的dict,您可使用它来存储任意数据(例如缓存计算值、设置标志等),谓词稍后能够在链中使用它。在谓词函数内部,能够这样使用:

A new context is created as a result of invoking Predicate.test() and is only valid for the duration of the invocation. A context is a simple dict that you can use to store arbitrary data, (eg. caching computed values, setting flags, etc.), that can be used by predicates later on in the chain. Inside a predicate function it can be used like so:

>>> @predicate ... def mypred(a, b): ... value = compute_expensive_value(a) ... mypred.context['value'] = value ... return True 

Other predicates can later use stored values:

>>> @predicate ... def myotherpred(a, b): ... value = myotherpred.context.get('value') ... if value is not None: ... return do_something_with_value(value) ... else: ... return do_something_without_value() 

Predicate.context provides a single args attribute that contains the arguments as given to test() at the beginning of the invocation.

Binding “self”

在谓词的函数体中,能够经过其名称引用谓词实例自己,例如is_book_author。将bind=true做为关键字参数传递给谓词修饰器将容许您使用self引用谓词,这更方便。束缚自我只是句法上的糖分。事实上,如下两项是等效的:

In a predicate’s function body, you can refer to the predicate instance itself by its name, eg. is_book_author. Passing bind=True as a keyword argument to the predicate decorator will let you refer to the predicate with self, which is more convenient. Binding self is just syntactic sugar. As a matter of fact, the following two are equivalent:

>>> @predicate ... def is_book_author(user, book): ... if is_book_author.context.args: ... return user == book.author ... return False >>> @predicate(bind=True) ... def is_book_author(self, user, book): ... if self.context.args: ... return user == book.author ... return False 

Skipping predicates

You may skip evaluation by returning None from your predicate:

>>> @predicate(bind=True) ... def is_book_author(self, user, book): ... if len(self.context.args) > 1: ... return user == book.author ... else: ... return None 

Returning None signifies that the predicate need not be evaluated, thus leaving the predicate result up to that point unchanged.

Logging predicate evaluation

能够选择将规则配置为在计算规则以帮助调试谓词时记录调试信息。消息在调试级别发送到“rules”记录器。如下dictconfigconfigures配置一个控制台记录器(若是在Django中使用规则,请将其放在项目的settings.py中):

rules can optionally be configured to log debug information as rules are evaluated to help with debugging your predicates. Messages are sent at the DEBUG level to the 'rules' logger. The following dictConfigconfigures a console logger (place this in your project’s settings.py if you’re using rules with Django):

LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', }, }, 'loggers': { 'rules': { 'handlers': ['console'], 'level': 'DEBUG', 'propagate': True, }, }, } 

When this logger is active each individual predicate will have a log message printed when it is evaluated.

Best practices

在测试规则以前,必须用规则集注册这些规则,为此,必须导入包含规则定义的模块。

对于具备多个谓词和规则的复杂项目,在一个模块中定义全部谓词和规则可能并不实际。最好将它们分割到项目的任何子组件中。在Django上下文中,这些子组件多是项目的应用程序。

另外一方面,因为从各地导入谓词以定义规则会致使循环导入和心碎,所以最好在不一样模块中进一步拆分谓词和规则。

能够选择将规则配置为应用程序中的autodiscover rules.py模块,并在启动时导入它们。要让规则这样作,只需编辑已安装的应用程序设置:

Before you can test for rules, these rules must be registered with a rule set, and for this to happen the modules containing your rule definitions must be imported.

For complex projects with several predicates and rules, it may not be practical to define all your predicates and rules inside one module. It might be best to split them among any sub-components of your project. In a Django context, these sub-components could be the apps for your project.

On the other hand, because importing predicates from all over the place in order to define rules can lead to circular imports and broken hearts, it’s best to further split predicates and rules in different modules.

rules may optionally be configured to autodiscover rules.py modules in your apps and import them at startup. To have rules do so, just edit your INSTALLED_APPS setting:

INSTALLED_APPS = ( # replace 'rules' with: 'rules.apps.AutodiscoverRulesConfig', ) 

Note: On Python 2, you must also add the following to the top of your rules.py file, or you’ll get import errors trying to import rules itself:

from __future__ import absolute_import 

API Reference

核心API能够从根规则模块访问。管理和视图的Django特定功能可从rules.contrib得到。

The core APIs are accessible from the root rules module. Django-specific functionality for the Admin and views is available from rules.contrib.

Class rules.Predicate

You create Predicate instances by passing in a callable:

>>> def is_book_author(user, book): ... return book.author == user ... >>> pred = Predicate(is_book_author) >>> pred <Predicate:is_book_author object at 0x10eeaa490> 

You may optionally provide a different name for the predicate that is used when inspecting it:

>>> pred = Predicate(is_book_author, name='another_name') >>> pred <Predicate:another_name object at 0x10eeaa490> 

Also, you may optionally provide bind=True in order to be able to access the predicate instance with self:

>>> def is_book_author(self, user, book): ... if self.context.args: ... return user == book.author ... return False ... >>> pred = Predicate(is_book_author, bind=True) >>> pred <Predicate:is_book_author object at 0x10eeaa490> 

Instance methods

test(obj=None, target=None)
Returns the result of calling the passed in callable with zero, one or two positional arguments, depending on how many it accepts.

Class rules.RuleSet

RuleSet extends Python’s built-in dict type. Therefore, you may create and use a rule set any way you’d use a dict.

Instance methods

add_rule(name, predicate)
Adds a predicate to the rule set, assigning it to the given rule name. Raises  KeyError if another rule with that name already exists.
set_rule(name, predicate)
Set the rule with the given name, regardless if one already exists.
remove_rule(name)
Remove the rule with the given name. Raises  KeyError if a rule with that name does not exist.
rule_exists(name)
Returns  True if a rule with the given name exists,  False otherwise.
test_rule(name, obj=None, target=None)
Returns the result of calling  predicate.test(obj, target) where  predicate is the predicate for the rule with the given name. Returns  False if a rule with the given name does not exist.

Decorators

@predicate

Decorator that creates a predicate out of any callable:

>>> @predicate ... def is_book_author(user, book): ... return book.author == user ... >>> is_book_author <Predicate:is_book_author object at 0x10eeaa490> 

Customising the predicate name:

>>> @predicate(name='another_name') ... def is_book_author(user, book): ... return book.author == user ... >>> is_book_author <Predicate:another_name object at 0x10eeaa490> 

Binding self:

>>> @predicate(bind=True) ... def is_book_author(self, user, book): ... if 'user_has_special_flag' in self.context: ... return self.context['user_has_special_flag'] ... return book.author == user 

Predefined predicates

always_allow()always_true()
Always returns  True.
always_deny()always_false()
Always returns  False.
is_authenticated(user)
Returns the result of calling  user.is_authenticated(). Returns  False if the given user does not have an  is_authenticated method.
is_superuser(user)
Returns the result of calling  user.is_superuser. Returns  False if the given user does not have an  is_superuser property.
is_staff(user)
Returns the result of calling  user.is_staff. Returns  False if the given user does not have an  is_staffproperty.
is_active(user)
Returns the result of calling  user.is_active. Returns  False if the given user does not have an  is_active property.
is_group_member(*groups)
Factory that creates a new predicate that returns  True if the given user is a member of  all the given groups,  False otherwise.

Shortcuts

Managing the shared rule set

add_rule(name, predicate)
Adds a rule to the shared rule set. See  RuleSet.add_rule.
set_rule(name, predicate)
Set the rule with the given name from the shared rule set. See  RuleSet.set_rule.
remove_rule(name)
Remove a rule from the shared rule set. See  RuleSet.remove_rule.
rule_exists(name)
Returns whether a rule exists in the shared rule set. See  RuleSet.rule_exists.
test_rule(name, obj=None, target=None)
Tests the rule with the given name. See  RuleSet.test_rule.

Managing the permissions rule set

add_perm(name, predicate)
Adds a rule to the permissions rule set. See  RuleSet.add_rule.
set_perm(name, predicate)
Replace a rule from the permissions rule set. See  RuleSet.set_rule.
remove_perm(name)
Remove a rule from the permissions rule set. See  RuleSet.remove_rule.
perm_exists(name)
Returns whether a rule exists in the permissions rule set. See  RuleSet.rule_exists.
has_perm(name, user=None, obj=None)
Tests the rule with the given name. See  RuleSet.test_rule.

Licence

django-rules is distributed under the MIT licence.

Copyright (c) 2014 Akis Kesoglou

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

相关文章
相关标签/搜索