[permission] Add F object support

This commit is contained in:
Benjamin Graillot 2020-02-13 15:59:19 +01:00
parent 2b49effebb
commit 982a5ae009
1 changed files with 38 additions and 8 deletions

View File

@ -5,7 +5,7 @@ import operator
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models 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 _ 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 # A json encoded Q object with the following grammar
# query -> [] | {} (the empty query representing all objects) # query -> [] | {} (the empty query representing all objects)
# query -> ['AND', query, …] # query -> ['AND', query, …] AND multiple queries
# -> ['OR', query, …] # | ['OR', query, …] OR multiple queries
# -> ['NOT', query] # | ['NOT', query] Opposite of query
# query -> {key: value, …} # query -> {key: value, …} A list of fields and values of a Q object
# key -> string # key -> string A field name
# value -> int | string | bool | null # value -> int | string | bool | null Literal values
# -> [parameter] # | [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: # Examples:
# Q(is_admin=True) := {'is_admin': ['TYPE', 'bool', 'True']} # Q(is_admin=True) := {'is_admin': ['TYPE', 'bool', 'True']}
@ -88,6 +95,26 @@ class Permission(models.Model):
self.full_clean() self.full_clean()
super().save() 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): def _about(_self, _query, **kwargs):
self = _self self = _self
query = _query query = _query
@ -110,6 +137,9 @@ class Permission(models.Model):
if isinstance(value, list): if isinstance(value, list):
# It is a parameter we query its primary key # It is a parameter we query its primary key
q_kwargs[key] = kwargs[value[0]].pk 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: else:
q_kwargs[key] = value q_kwargs[key] = value
return Q(**q_kwargs) return Q(**q_kwargs)