Improve the resolve_attribute function

Update the arguments of the function to be full length and more explicit.
Use exceptions to find the correct way of using the attribute of the
object passed instead of trying to use introspection.
Add support for passing key word arguments to the attribute being
resolved even if it is a class method.

Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
Roberto Rosario
2018-12-21 23:41:38 -04:00
parent 9bcaf1849b
commit 79cba7abe1
4 changed files with 37 additions and 29 deletions

View File

@@ -46,7 +46,7 @@ class AccessControlListManager(models.Manager):
stored_permissions = (permissions.stored_permission,)
if related:
obj = resolve_attribute(obj, related)
obj = resolve_attribute(obj=obj, attribute=related)
try:
parent_accessor = ModelPermission.get_inheritance(
@@ -200,7 +200,7 @@ class AccessControlListManager(models.Manager):
else:
try:
parent_object = resolve_attribute(
obj=instance, attrib=parent_accessor
obj=instance, attribute=parent_accessor
)
except AttributeError:
# Parent accessor is not an attribute, try it as a related

View File

@@ -43,7 +43,7 @@ class DetailForm(forms.ModelForm):
super(DetailForm, self).__init__(*args, **kwargs)
for extra_field in self.extra_fields:
result = resolve_attribute(self.instance, extra_field['field'])
result = resolve_attribute(obj=self.instance, attribute=extra_field['field'])
label = 'label' in extra_field and extra_field['label'] or None
# TODO: Add others result types <=> Field types
if isinstance(result, models.query.QuerySet):
@@ -54,8 +54,8 @@ class DetailForm(forms.ModelForm):
self.fields[extra_field['field']] = forms.CharField(
label=extra_field['label'],
initial=resolve_attribute(
self.instance,
extra_field['field'], None
obj=self.instance,
attribute=extra_field['field']
),
widget=extra_field.get('widget', PlainWidget)
)

View File

@@ -32,8 +32,8 @@ def get_collections():
@register.filter
def get_encoded_parameter(item, parameters_dict):
result = {}
for attrib_name, attrib in parameters_dict.items():
result[attrib_name] = resolve_attribute(item, attrib)
for key, value in parameters_dict.items():
result[key] = resolve_attribute(obj=item, attribute=value)
return dumps(result)
@@ -44,7 +44,7 @@ def get_type(value):
@register.filter
def object_property(value, arg):
return resolve_attribute(value, arg)
return resolve_attribute(obj=value, attribute=arg)
@register.simple_tag

View File

@@ -130,29 +130,37 @@ def resolve(path, urlconf=None):
return django_resolve(path=path, urlconf=urlconf)
def resolve_attribute(obj, attrib, arguments=None):
if isinstance(attrib, types.FunctionType):
return attrib(obj)
elif isinstance(obj, dict_type) or isinstance(obj, dictionary_type):
return obj[attrib]
else:
def resolve_attribute(obj, attribute, kwargs=None):
if not kwargs:
kwargs = {}
# Try as a callable
try:
return attribute(obj, **kwargs)
except TypeError:
# Try as a dictionary
try:
result = reduce_function(getattr, attrib.split('.'), obj)
if isinstance(result, types.MethodType):
if arguments:
return result(**arguments)
return obj[attribute]
except TypeError:
try:
# If there are dots in the attribute name, traverse them
# to the final attribute
result = reduce_function(getattr, attribute.split('.'), obj)
try:
# Try it as a method
return result(**kwargs)
except TypeError:
# Try it as a property
return result
except AttributeError:
# Try as a related model field
if LOOKUP_SEP in attribute:
attrib = attribute.replace(LOOKUP_SEP, '.')
return resolve_attribute(
obj=obj, attribute=attribute, kwargs=kwargs
)
else:
return result()
else:
return result
except AttributeError:
if LOOKUP_SEP in attrib:
attrib = attrib.replace(LOOKUP_SEP, '.')
return resolve_attribute(
obj=obj, attrib=attrib, arguments=arguments
)
else:
raise
raise
def return_related(instance, related_field):