Add OR and AND operations for Q objects

This commit is contained in:
pv.larkin 2017-11-29 14:27:54 +03:00
parent ca8586b213
commit 0342dc863b

View File

@ -143,9 +143,23 @@ class FOV(object):
class Q(object): class Q(object):
def __init__(self, **kwargs): AND_MODE = 'AND'
self._fovs = [self._build_fov(k, v) for k, v in six.iteritems(kwargs)] OR_MODE = 'OR'
def __init__(self, **filter_fields):
self._fovs = [self._build_fov(k, v) for k, v in six.iteritems(filter_fields)]
self._l_child = None
self._r_child = None
self._negate = False self._negate = False
self._mode = self.AND_MODE
@classmethod
def _construct_from(cls, l_child, r_child, mode):
q = Q()
q._l_child = l_child
q._r_child = r_child
q._mode = mode
return q
def _build_fov(self, key, value): def _build_fov(self, key, value):
if '__' in key: if '__' in key:
@ -155,13 +169,23 @@ class Q(object):
return FOV(field_name, operator, value) return FOV(field_name, operator, value)
def to_sql(self, model_cls): def to_sql(self, model_cls):
if not self._fovs: if self._fovs:
return '1' sql = ' {} '.format(self._mode).join(fov.to_sql(model_cls) for fov in self._fovs)
sql = ' AND '.join(fov.to_sql(model_cls) for fov in self._fovs) else:
if self._l_child and self._r_child:
sql = '({}) {} ({})'.format(self._l_child.to_sql(model_cls), self._mode, self._r_child.to_sql(model_cls))
else:
return '1'
if self._negate: if self._negate:
sql = 'NOT (%s)' % sql sql = 'NOT (%s)' % sql
return sql return sql
def __or__(self, other):
return Q._construct_from(self, other, self.OR_MODE)
def __and__(self, other):
return Q._construct_from(self, other, self.AND_MODE)
def __invert__(self): def __invert__(self):
q = copy(self) q = copy(self)
q._negate = True q._negate = True
@ -286,20 +310,24 @@ class QuerySet(object):
qs._fields = field_names qs._fields = field_names
return qs return qs
def filter(self, **kwargs): def filter(self, *q, **filter_fields):
""" """
Returns a copy of this queryset that includes only rows matching the conditions. Returns a copy of this queryset that includes only rows matching the conditions.
Add q object to query if it specified.
""" """
qs = copy(self) qs = copy(self)
qs._q = list(self._q) + [Q(**kwargs)] if q:
qs._q = list(self._q) + list(q)
else:
qs._q = list(self._q) + [Q(**filter_fields)]
return qs return qs
def exclude(self, **kwargs): def exclude(self, **filter_fields):
""" """
Returns a copy of this queryset that excludes all rows matching the conditions. Returns a copy of this queryset that excludes all rows matching the conditions.
""" """
qs = copy(self) qs = copy(self)
qs._q = list(self._q) + [~Q(**kwargs)] qs._q = list(self._q) + [~Q(**filter_fields)]
return qs return qs
def paginate(self, page_num=1, page_size=100): def paginate(self, page_num=1, page_size=100):