diff --git a/apps/permission/models.py b/apps/permission/models.py index 5c016806..2ca17e4c 100644 --- a/apps/permission/models.py +++ b/apps/permission/models.py @@ -5,7 +5,7 @@ import operator from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.db import models -from django.db.models import Q +from django.db.models import F, Q from django.utils.translation import gettext_lazy as _ @@ -58,13 +58,20 @@ class Permission(models.Model): # A json encoded Q object with the following grammar # query -> [] | {} (the empty query representing all objects) - # query -> ['AND', query, …] - # -> ['OR', query, …] - # -> ['NOT', query] - # query -> {key: value, …} - # key -> string - # value -> int | string | bool | null - # -> [parameter] + # query -> ['AND', query, …] AND multiple queries + # | ['OR', query, …] OR multiple queries + # | ['NOT', query] Opposite of query + # query -> {key: value, …} A list of fields and values of a Q object + # key -> string A field name + # value -> int | string | bool | null Literal values + # | [parameter] A parameter + # | {'F': oper} An F object + # oper -> [string] A parameter + # | ['ADD', oper, …] Sum multiple F objects or literal + # | ['SUB', oper, oper] Substract two F objects or literal + # | ['MUL', oper, …] Multiply F objects or literals + # | int | string | bool | null Literal values + # | ['F', string] A field # # Examples: # Q(is_admin=True) := {'is_admin': ['TYPE', 'bool', 'True']} @@ -88,6 +95,26 @@ class Permission(models.Model): self.full_clean() super().save() + @staticmethod + def compute_f(_oper, **kwargs): + oper = _oper + if isinstance(oper, list): + if len(oper) == 1: + return kwargs[oper[0]].pk + elif len(oper) >= 2: + if oper[0] == 'ADD': + return functools.reduce(operator.add, [compute_f(oper, **kwargs) for oper in oper[1:]]) + elif oper[0] == 'SUB': + return compute_f(oper[1], **kwargs) - compute_f(oper[2], **kwargs) + elif oper[0] == 'MUL': + return functools.reduce(operator.mul, [compute_f(oper, **kwargs) for oper in oper[1:]]) + elif oper[0] == 'F': + return F(oper[1]) + else: + return oper + # TODO: find a better way to crash here + raise Exception("F is wrong") + def _about(_self, _query, **kwargs): self = _self query = _query @@ -110,6 +137,9 @@ class Permission(models.Model): if isinstance(value, list): # It is a parameter we query its primary key q_kwargs[key] = kwargs[value[0]].pk + elif isinstance(value, dict): + # It is an F object + q_kwargs[key] = compute_f(query['F'], **kwargs) else: q_kwargs[key] = value return Q(**q_kwargs)