Skip to content

Use of proxy models for Allocation and Project #317

@cc-a

Description

@cc-a

The Coldfront data model is fairly complex and we make heavy use of the AllocationAttribute and ProjectAttribute models in order to attache the the metadata we need to Allocations and Projects. This leads to fairly verbose queries e.g.:

shortname = allocation.allocationattribute_set.get(
       allocation_attribute_type__name="Shortname"
).value

peppered throughout the code. We also expect that certain attributes are present and don't always have consistent error checking.

It would be very nice to have a consistent interface that improves code readability and makes sure error handling is done consistently. I believe this can be achieved through the following pattern using django proxy models. This would look like:

class RDFAllocation(Allocation):
    """Proxy model for RDF Active allocations."""

    class Meta:
        """Meta class for RDFAllocation."""

        proxy = True

    def __init__(self, *args, **kwargs):
        """Ensure RDFAllocation is associated with the 'RDF Active' resource."""
        super().__init__(*args, **kwargs)
        resource = self.get_parent_resource
        if not resource or resource.name != "RDF Active":
            raise ValueError(
                "RDFAllocation must be associated with the 'RDF Active' resource"
            )

    def _get_attribute_value(self, attribute_name: str) -> Any:
        try:
            return self.allocationattribute_set.get(
                allocation_attribute_type__name=attribute_name
            ).typed_value()
        except AllocationAttribute.MultipleObjectsReturned:
            raise ValueError(f"Multiple {attribute_name} attributes found for allocation - {self}")
        except AllocationAttribute.DoesNotExist:
            raise AttributeError(
                f"{attribute_name} attribute not found for this allocation"
            )

    @property
    def shortname(self) -> str:
        """Get the shortname of the allocation."""
        return self._get_attribute_value("Shortname")

When working with relevant allocations one simply uses RDFAllocation instead of Allocation. The database actions it performs are completely equivalent but the Python objects returned have the additional methods.

This issue covers implementing the RDFAllocation model with the set of required properties for the expected attributes and testing it. It should also go through the code base to make use the new model where it makes sense. It should also consider implementing an equivalent model for Project but this may be of more limited value as we don't access ProjectAttribute values nearly as often.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions