dfunckt / django-rules

Awesome Django authorization, without the database

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Optimize rule evaluation

danielnaab opened this issue · comments

Assume a rule such as:

my_rule = first_condition | second_condition

I would assume that second_condition would not be evaluated if first_condition evaluated to True. It appears that django-rules doesn't doesn't apply this optimization.

Is there a way to handle this with the library? If not, what would be involved in adding it? (This is particularly valuable for my use-case, as I am abusing the predicates to mutate data when the predicates are called.)

Thanks for a great library!

I see how Predicate._combine works and how it would be difficult to do any optimizations here, especially since the native bitwise OR will evaluate both sides.

I'll probably just stop "abusing" the library and work around this - but I would be curious if it's possible to handle this on the Predicate level in some manner.

Your assumption that the right-hand side of the expression should not be evaluated is correct, and this used to be the case until dc8b561, which introduced this regression. It's unfortunate that it went unnoticed for so long -- thanks for finding this out!

I just released a new version (v1.1.0) that you may install from PyPi or master that restores short-circuiting for evaluation (among a couple of other changes -- have a look at changelog).

BTW, what's your (ab)use case? Have you checked whether you may accomplish the same thing by storing values in the invocation context?

Awesome @dfunckt, thank you!

For my (ab)use case: I considered using the invocation context, but it was cleaner to modify the model instance being evaluated. Basically, I've got a large collection of predicates (one for each object type) that get combined into one master predicate that is used to determine if the context of an object is legal. Each object is a node in a tree, and certain node types can only exist in certain circumstances. In addition, rules are applied that do things like zero out certain fields if a user doesn't have permission to view them, or inherit attributes from parent nodes when creating new nodes, but they should only be run when evaluating the appropriate type. So the master predicate looks something like:

(is_type_1 & (type_1_rule & [...]) | (is_type_2 & (type_2_rule & [...]) | [...]

These model instances are then fed through django-rest-framework's model viewsets/serializers, which would either return them or save them to the db. So while I could store some data in the invocation context and mutate the model instance after rule evaluation, it's a bit simpler to just do it directly.

Cool, sounds super-interesting! I've been looking for ways to better integrate rules with rest-framework, but haven't worked with it for a while to come up with anything useful.

Let me know if you think there's any way to improve rules that would help you out with this ;)

Thanks for your help and the excellent work on this library! I don't have any suggestions at this point, since I'm not actually using the Django integration at all, but I'm still working through the use cases and refactoring as I go along. Everything works great so far. But I'll keep in touch if I have any ideas. :)