from django.db import models
from .. import exc
from . import constants
from .attribute import Attribute
[docs]
class Value(models.Model):
"""Represents a value for an attribute attached to a Resource."""
attribute = models.ForeignKey(
"Attribute",
related_name="values",
db_index=True,
on_delete=models.PROTECT,
help_text="The Attribute to which this Value is assigned.",
)
value = models.CharField(
max_length=255,
null=False,
blank=True,
db_index=True,
help_text="The Attribute value.",
)
resource_id = models.IntegerField(
"Resource ID",
null=False,
help_text="The unique ID of the Resource to which the Value is bound.",
)
resource_name = models.CharField(
"Resource Type",
max_length=20,
null=False,
db_index=True,
choices=constants.RESOURCE_CHOICES,
help_text="The name of the Resource type to which the Value is bound.",
)
name = models.CharField(
"Name",
max_length=64,
null=False,
blank=True,
help_text=(
"The name of the Attribute to which the Value is bound. "
"(Internal use only)"
),
)
# We are currently inferring the site_id from the parent Attribute in
# .save() method. We don't want to even care about the site_id, but it
# simplifies managing them this way.
site = models.ForeignKey(
"Site",
db_index=True,
related_name="values",
on_delete=models.PROTECT,
verbose_name="Site",
help_text="Unique ID of the Site this Value is under.",
)
def __init__(self, *args, **kwargs):
self._obj = kwargs.pop("obj", None)
super().__init__(*args, **kwargs)
def __str__(self):
return "%s:%s %s=%s" % (
self.resource_name,
self.resource_id,
self.name,
self.value,
)
class Meta:
unique_together = ("name", "value", "resource_name", "resource_id")
# This is most commonly looked up
indexes = [
models.Index(fields=["name", "value", "resource_name"]),
models.Index(fields=["resource_name", "resource_id"]),
]
def clean_resource_name(self, value):
if value not in constants.VALID_ATTRIBUTE_RESOURCES:
raise exc.ValidationError("Invalid resource name: %r." % value)
return value
def clean_name(self, attr):
return attr.name
[docs]
def clean_site(self, value):
"""Always enforce that site is set."""
if value is None:
try:
return self.attribute.site_id
except Attribute.DoesNotExist:
return Attribute.objects.get(id=self.attribute_id).site_id
return value
[docs]
def clean_fields(self, exclude=None):
obj = self._obj
if obj is None:
return
self.site_id = self.clean_site(self.site_id)
self.resource_name = self.clean_resource_name(obj.__class__.__name__)
self.resource_id = obj.id
self.name = self.clean_name(self.attribute)
[docs]
def save(self, *args, **kwargs):
self.full_clean()
super().save(*args, **kwargs)
def to_dict(self):
return {
"id": self.id,
"name": self.name,
"value": self.value,
"attribute": self.attribute_id,
"resource_name": self.resource_name,
"resource_id": self.resource_id,
}