Flexible rules library for Django
Install:
pip install django-rules
Add rules
to INSTALLED_APPS
and run python manage.py syncdb
to
create the appropriate database tables.
Some projects require complex and abstract business logic like:
User A is allowed to buy product XYZ if attribute B of User A equals 58
You need then low level granularity rules. Some solutions exist adding permission to the object itself in the DB. But sometimes, we may want to get permissions for an object which is not in DB.
That's why rules!
In rules, you create a set of assumptions like your customer do. For example:
Anonymous users can't see products of type A
Customer can see standard products
Sales representatives can see VIP products
Admin can see everything
to translate that in python code, you have to define:
- a group
- Anonymous, Customer, Sales rep, Admin are all groups. They are dynamically assigned to users because they validate a condition. Something like you are anonymous if you are not authenticated. You can belong to multiple groups.
- a predicate
- Any products, standard products, VIP products, everything are all predicates. A predicate is a method to caracterise something. How do you know than a product is of type B: product_type=="B". To be efficient, there is many way to implement your predicate. As of now, three are supported. apply_qs for a queryset, apply_obj for an object, apply_sqs for a search_queryset coming from Haystack.
- a rule
- The link between the group and the predicate through an action. Action may be: 'see products' in our example. Action are only strings
Here is what a rule looks file:
Rule.objects.create_rule(groups_in="anonymous", cant_do="see_products", predicate="A_products")
translating:
Anonymous users can't see products of type A
You have then to explain to 'rules' what anynomous and A_products means by subclassing Group and Predicate.
- class Anonymous(Group):
name="anonymous" @classmethod def belong(cls, obj):
return isinstance(obj, User) and obj.is_authenticated()
then the Predicate.
- class Any(Predicate):
name="A_products" @classmethod def apply_qs(cls, qs):
return {"product_type":"A"}
then, to check for it, just do:
list_of_products = ApplyRule(on=Product.objects.all(), action="see_products", for_=anonymous_user).check()
And you will get a queryset with the list of product validating your rules. Let say you have 3 products, 1 of type A , 1 of type B and 1 of type C.
You get:
list_of_products = [ productB, productC ]
Now, if you add a new rule for C product:
Anonymous users can't see products of type C
you will get:
list_of_products = [ productB ]
You may want to decorate a view. There is an helper to do that simply. For example:
from rules.base import get_view_decorator
can_see_products = get_view_decorator("see_products")
@can_see_products def get(self, *args, **kwargs):
...
Install for contributing:
git clone git://github.com/anthony-tresontani/rules.git mkvirtualenv rules cd rules python setup.py develop pip install -r requirements.txt
Run tests:
./run_tests.py
Any object is automatically assigned a group by default containing the all objects of the same model. For ex, any Product instead will belong to the group group_product_model.