diff --git a/CHANGES.rst b/CHANGES.rst index 164bbf2..59e0598 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,7 @@ 2.6.0 (unreleased) ------------------ -- no changes yet +- #19 Prioritize getters instead of fieldnames on value retrieval from instance 2.5.0 (2024-01-03) diff --git a/src/senaite/app/supermodel/model.py b/src/senaite/app/supermodel/model.py index 8acdb10..7ce31ec 100644 --- a/src/senaite/app/supermodel/model.py +++ b/src/senaite/app/supermodel/model.py @@ -206,10 +206,34 @@ def items(self): return list(self.iteritems()) def get_field(self, name, default=None): - accessor = getattr(self.instance, "getField", None) - if accessor is None: + """Returns the instance's field that matches for the given name + """ + try: + fields = api.get_fields(self.instance) + except api.APIError: return default - return accessor(name) + + field = fields.get(name) + if field: + return field + + return default + + def get_field_value(self, name, default=None): + """Returns the value for the given name and current instance + """ + # always give priority to getters regardless of type + accessor_name = "get{}".format(name) + accessor = getattr(self.instance, accessor_name, _marker) + if accessor is not _marker: + return accessor() + + # rely on the fields + field = self.get_field(name) + if field: + return field.get(self.instance) + + return default def get(self, name, default=None): # Internal lookup in the data dict @@ -219,43 +243,31 @@ def get(self, name, default=None): if value is not _marker: return self.data[name] - # Field lookup on the instance - field = self.get_field(name) + # Try first to lookup the field value from the instance + value = self.get_field_value(name, default=_marker) - if field is None: + if value is _marker: # expose non-private members of the instance/brain to have access # to e.g. self.absolute_url (function object) or self.review_state - if not name.startswith("_") or not name.startswith("__"): - # check if the instance contains this attribute - instance = self.instance - instance_value = getattr(instance, name, _marker) - if instance_value is not _marker: - return instance_value - - # check if the brain contains this attribute - brain = self.brain - # NOTE: we might get no brain here if the object is temporary, - # e.g. during initialization! - if brain: - brain_value = getattr(brain, name, _marker) - if brain_value is not _marker: - return brain_value + if name.startswith("_"): + return default + + # check if the instance contains this attribute + instance = self.instance + instance_value = getattr(instance, name, _marker) + if instance_value is not _marker: + return instance_value + + # check if the brain contains this attribute + brain = self.brain + # NOTE: we might get no brain here if the object is temporary, + # e.g. during initialization! + if brain: + brain_value = getattr(brain, name, _marker) + if brain_value is not _marker: + return brain_value return default - else: - # Retrieve field value by accessor name - accessor = field.getAccessor(self.instance) - accessor_name = accessor.__name__ - - if self.is_temporary(self.instance) is False: - # Metadata lookup by accessor name - value = getattr(self.brain, accessor_name, _marker) - - if value is _marker: - logger.debug("Add metadata column '{}' to the catalog '{}' " - "to increase performance!" - .format(accessor_name, self.catalog.__name__)) - value = accessor() # Process value for publication value = self.process_value(value)