mirror of
https://github.com/jazzband/django-eav2.git
synced 2026-04-11 10:50:59 +00:00
refactored expand_filter_string from recursive to iterative
This commit is contained in:
parent
9361fbd7eb
commit
d6f59672ea
1 changed files with 61 additions and 37 deletions
98
managers.py
98
managers.py
|
|
@ -2,51 +2,75 @@ from django.db import models
|
|||
#from django.db.models import Aggregate
|
||||
from .models import EavAttribute
|
||||
|
||||
|
||||
def expand_filter_string(model_cls, q_str, prefix='', extra_filters=None):
|
||||
def expand_filter_string(q_str, root_cls):
|
||||
from .utils import EavRegistry
|
||||
if not extra_filters:
|
||||
extra_filters = {}
|
||||
fields = q_str.split('__')
|
||||
extra_filters = {}
|
||||
# q_str is a filter argument, something like:
|
||||
# zoo__animal__food__eav__vitamin_c__gt, where
|
||||
# in this example 'food' would be registered as an entity,
|
||||
# 'eav' is the proxy field name, and 'contains_vitamin_c' is the
|
||||
# attribute. So once we split the above example would be a list with
|
||||
# something we can easily process:
|
||||
# ['zoo','animal','food','eav','vitamin_c', 'gt']
|
||||
filter_tokens = q_str.split('__')
|
||||
current_cls = root_cls
|
||||
upperbound = len(filter_tokens) - 1
|
||||
i = 0
|
||||
while i < upperbound:
|
||||
current_cls_config = EavRegistry.get_config_cls_for_model(current_cls)
|
||||
if current_cls_config and filter_tokens[i] == current_cls_config.proxy_field_name:
|
||||
gr_field = current_cls_config.generic_relation_field_name
|
||||
# this will always work, because we're iterating over the length of the tokens - 1.
|
||||
# if someone just specifies 'zoo__animal_food__eav' as a filter,
|
||||
# they'll get the appropriate error: column 'eav' doesn't existt (i.e. if i != 0),
|
||||
# prepend all the
|
||||
slug = filter_tokens[i + 1]
|
||||
datatype = EavAttribute.objects.get(slug=slug).datatype
|
||||
extra_filter_key = '%s__attribute__slug' % gr_field
|
||||
# if we're somewhere in the middle of this filter argument
|
||||
# joins up to this point
|
||||
if i != 0:
|
||||
extra_filter_key = '%s__%s' % ('__'.join(filter_tokens[0:i]), extra_filter_key)
|
||||
extra_filters[extra_filter_key] = slug
|
||||
# modify the filter argument in-place, expanding 'eav' into 'eav_values__value_<datatype>'
|
||||
filter_tokens = filter_tokens[0:i] + [gr_field, 'value_%s' % datatype] + filter_tokens[i + 2:]
|
||||
# this involves a little indexing voodoo, because we inserted two elements in place of one
|
||||
# original element
|
||||
i += 1
|
||||
upperbound += 1
|
||||
# filter_tokens[0] = "%s__value_%s" % (gr_field, datatype)
|
||||
else:
|
||||
direct = False
|
||||
# Is it not EAV, but also not another field?
|
||||
try:
|
||||
field_object, model, direct, m2m = current_cls._meta.get_field_by_name(filter_tokens[0])
|
||||
# If we've hit the end, i.e. a simple column attribute like IntegerField or Boolean Field,
|
||||
# we're done modifying the tokens, so we can break out of the loop early
|
||||
if direct:
|
||||
return '__'.join(filter_tokens), extra_filters
|
||||
else:
|
||||
# It is a foreign key to some other model, so we need to keep iterating, looking for registered
|
||||
# entity models to expand
|
||||
current_cls = field_object.model
|
||||
except models.FieldDoesNotExist:
|
||||
# this is a bogus filter anyway on a non-existent attribute, let the call to the super filter throw the
|
||||
# appropriate error
|
||||
return '__'.join(filter_tokens), extra_filters
|
||||
|
||||
# Is it EAV?
|
||||
config_cls = EavRegistry.get_config_cls_for_model(model_cls)
|
||||
if len(fields) > 1 and config_cls and fields[0] == config_cls.proxy_field_name:
|
||||
gr_field = config_cls.generic_relation_field_name
|
||||
slug = fields[1]
|
||||
datatype = EavAttribute.objects.get(slug=slug).datatype
|
||||
extra_filter_key = '%s__attribute__slug' % gr_field
|
||||
if prefix:
|
||||
extra_filter_key = '%s__%s' % (prefix, extra_filter_key)
|
||||
extra_filters[extra_filter_key] = slug
|
||||
fields[0] = "%s__value_%s" % (gr_field, datatype)
|
||||
fields.pop(1)
|
||||
return '__'.join(fields), extra_filters
|
||||
|
||||
|
||||
direct = False
|
||||
# Is it not EAV, but also not another field?
|
||||
try:
|
||||
field_object, model, direct, m2m = model_cls._meta.get_field_by_name(fields[0])
|
||||
except models.FieldDoesNotExist:
|
||||
return q_str, extra_filters
|
||||
|
||||
# Is it a direct field?
|
||||
if direct:
|
||||
return q_str, extra_filters
|
||||
else:
|
||||
# It is a foreign key.
|
||||
prefix = "%s__%s" % (prefix, fields[0]) if prefix else fields[0]
|
||||
sub_q_str = '__'.join(fields[1:])
|
||||
retstring, dictionary = self.expand_filter_string(field_object.model, sub_q_str, prefix, extra_filters)
|
||||
return ("%s__%s" % (fields[0], retstring), dictionary)
|
||||
# regular loop forward
|
||||
i += 1
|
||||
# at the end of the day, we return the modified keyword filter, and any additional filters needed to make this
|
||||
# query work, for passing up to the super call to filter()
|
||||
return '__'.join(filter_tokens), extra_filters
|
||||
|
||||
class EntityManager(models.Manager):
|
||||
def filter(self, *args, **kwargs):
|
||||
qs = self.get_query_set().filter(*args)
|
||||
cls = self.model
|
||||
for lookup, value in kwargs.items():
|
||||
updated_lookup, extra_filters = expand_filter_string(cls, lookup)
|
||||
print "In %s" % lookup
|
||||
updated_lookup, extra_filters = expand_filter_string(lookup, cls)
|
||||
print "Out %s %s" % (updated_lookup, extra_filters)
|
||||
extra_filters.update({updated_lookup: value})
|
||||
qs = qs.filter(**extra_filters)
|
||||
return qs
|
||||
|
|
|
|||
Loading…
Reference in a new issue