Rewrite translate_polymorphic_field_path() avoid closures

Inspired by PR #259 to look at this
fix_request_path_info
Diederik van der Boor 2019-07-12 11:37:13 +02:00
parent 9225f08141
commit 17e41c4f7f
No known key found for this signature in database
GPG Key ID: 4FA014E0305E73C1
1 changed files with 47 additions and 32 deletions

View File

@ -5,9 +5,11 @@ PolymorphicQuerySet support functions
from __future__ import absolute_import from __future__ import absolute_import
import copy import copy
from collections import deque
from django.apps import apps from django.apps import apps
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import FieldError
from django.db import models from django.db import models
from django.db.models import Q from django.db.models import Q
from django.db.models.fields.related import ForeignObjectRel, RelatedField from django.db.models.fields.related import ForeignObjectRel, RelatedField
@ -166,41 +168,10 @@ def translate_polymorphic_field_path(queryset_model, field_path):
except models.FieldDoesNotExist: except models.FieldDoesNotExist:
pass pass
# function to collect all sub-models, this should be optimized (cached) submodels = _get_all_sub_models(queryset_model)
def add_all_sub_models(model, result):
if issubclass(model, models.Model) and model != models.Model:
# model name is occurring twice in submodel inheritance tree => Error
if model.__name__ in result and model != result[model.__name__]:
e = 'PolymorphicModel: model name alone is ambiguous: %s.%s and %s.%s!\n'
e += 'In this case, please use the syntax: applabel__ModelName___field'
assert model, e % (
model._meta.app_label, model.__name__,
result[model.__name__]._meta.app_label, result[model.__name__].__name__)
result[model.__name__] = model
for b in model.__subclasses__():
add_all_sub_models(b, result)
submodels = {}
add_all_sub_models(queryset_model, submodels)
model = submodels.get(classname, None) model = submodels.get(classname, None)
assert model, 'PolymorphicModel: model %s not found (not a subclass of %s)!' % (classname, queryset_model.__name__) assert model, 'PolymorphicModel: model %s not found (not a subclass of %s)!' % (classname, queryset_model.__name__)
# create new field path for expressions, e.g. for baseclass=ModelA, myclass=ModelC
# 'modelb__modelc" is returned
def _create_base_path(baseclass, myclass):
bases = myclass.__bases__
for b in bases:
if b == baseclass:
return myclass.__name__.lower()
path = _create_base_path(baseclass, b)
if path:
if b._meta.abstract or b._meta.proxy:
return myclass.__name__.lower()
return path + '__' + myclass.__name__.lower()
return ''
basepath = _create_base_path(queryset_model, model) basepath = _create_base_path(queryset_model, model)
if negated: if negated:
@ -216,6 +187,50 @@ def translate_polymorphic_field_path(queryset_model, field_path):
return newpath return newpath
def _get_all_sub_models(base_model):
"""#Collect all sub-models, this should be optimized (cached)"""
result = {}
queue = deque([base_model])
while queue:
model = queue.popleft()
if issubclass(model, models.Model) and model != models.Model:
# model name is occurring twice in submodel inheritance tree => Error
if model.__name__ in result and model != result[model.__name__]:
raise FieldError(
'PolymorphicModel: model name alone is ambiguous: %s.%s and %s.%s match!\n'
'In this case, please use the syntax: applabel__ModelName___field' % (
model._meta.app_label, model.__name__,
result[model.__name__]._meta.app_label,
result[model.__name__].__name__
)
)
result[model.__name__] = model
queue.extend(model.__subclasses__())
return result
def _create_base_path(baseclass, myclass):
# create new field path for expressions, e.g. for baseclass=ModelA, myclass=ModelC
# 'modelb__modelc" is returned
bases = myclass.__bases__
for b in bases:
if b == baseclass:
return _get_query_related_name(myclass)
path = _create_base_path(baseclass, b)
if path:
if b._meta.abstract or b._meta.proxy:
return myclass.__name__.lower()
return path + '__' + _get_query_related_name(myclass)
return ''
def _get_query_related_name(myclass):
return myclass.__name__.lower()
def create_instanceof_q(modellist, not_instance_of=False, using=DEFAULT_DB_ALIAS): def create_instanceof_q(modellist, not_instance_of=False, using=DEFAULT_DB_ALIAS):
""" """
Helper function for instance_of / not_instance_of Helper function for instance_of / not_instance_of