From 79cba7abe1e6d9864667921aff02385049523753 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Fri, 21 Dec 2018 23:41:38 -0400 Subject: [PATCH] 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 --- mayan/apps/acls/managers.py | 4 +- mayan/apps/common/forms.py | 6 +-- mayan/apps/common/templatetags/common_tags.py | 6 +-- mayan/apps/common/utils.py | 50 +++++++++++-------- 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/mayan/apps/acls/managers.py b/mayan/apps/acls/managers.py index a4da3a0e85..40a44f653b 100644 --- a/mayan/apps/acls/managers.py +++ b/mayan/apps/acls/managers.py @@ -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 diff --git a/mayan/apps/common/forms.py b/mayan/apps/common/forms.py index ceb6e61c11..37f566727b 100644 --- a/mayan/apps/common/forms.py +++ b/mayan/apps/common/forms.py @@ -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) ) diff --git a/mayan/apps/common/templatetags/common_tags.py b/mayan/apps/common/templatetags/common_tags.py index 71b1606d35..3c50bf2a27 100644 --- a/mayan/apps/common/templatetags/common_tags.py +++ b/mayan/apps/common/templatetags/common_tags.py @@ -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 diff --git a/mayan/apps/common/utils.py b/mayan/apps/common/utils.py index e931b2c34b..e26fa28575 100644 --- a/mayan/apps/common/utils.py +++ b/mayan/apps/common/utils.py @@ -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):