Feature: Implement custom fields for documents (#4502)

Adds custom fields of certain data types, attachable to documents and searchable

Co-Authored-By: Trenton H <797416+stumpylog@users.noreply.github.com>
This commit is contained in:
shamoon
2023-11-05 17:26:51 -08:00
parent 800f54f263
commit 10729f0362
67 changed files with 3199 additions and 421 deletions

View File

@@ -877,9 +877,139 @@ class ConsumptionTemplate(models.Model):
return f"{self.name}"
class CustomField(models.Model):
"""
Defines the name and type of a custom field
"""
class FieldDataType(models.TextChoices):
STRING = ("string", _("String"))
URL = ("url", _("URL"))
DATE = ("date", _("Date"))
BOOL = ("boolean"), _("Boolean")
INT = ("integer", _("Integer"))
FLOAT = ("float", _("Float"))
MONETARY = ("monetary", _("Monetary"))
created = models.DateTimeField(
_("created"),
default=timezone.now,
db_index=True,
editable=False,
)
name = models.CharField(max_length=128)
data_type = models.CharField(
_("data type"),
max_length=50,
choices=FieldDataType.choices,
editable=False,
)
class Meta:
ordering = ("created",)
verbose_name = _("custom field")
verbose_name_plural = _("custom fields")
constraints = [
models.UniqueConstraint(
fields=["name"],
name="%(app_label)s_%(class)s_unique_name",
),
]
def __str__(self) -> str:
return f"{self.name} : {self.data_type}"
class CustomFieldInstance(models.Model):
"""
A single instance of a field, attached to a CustomField for the name and type
and attached to a single Document to be metadata for it
"""
created = models.DateTimeField(
_("created"),
default=timezone.now,
db_index=True,
editable=False,
)
document = models.ForeignKey(
Document,
blank=False,
null=False,
on_delete=models.CASCADE,
related_name="custom_fields",
editable=False,
)
field = models.ForeignKey(
CustomField,
blank=False,
null=False,
on_delete=models.CASCADE,
related_name="fields",
editable=False,
)
# Actual data storage
value_text = models.CharField(max_length=128, null=True)
value_bool = models.BooleanField(null=True)
value_url = models.URLField(null=True)
value_date = models.DateField(null=True)
value_int = models.IntegerField(null=True)
value_float = models.FloatField(null=True)
value_monetary = models.DecimalField(null=True, decimal_places=2, max_digits=12)
class Meta:
ordering = ("created",)
verbose_name = _("custom field instance")
verbose_name_plural = _("custom field instances")
constraints = [
models.UniqueConstraint(
fields=["document", "field"],
name="%(app_label)s_%(class)s_unique_document_field",
),
]
def __str__(self) -> str:
return str(self.field.name) + f" : {self.value}"
@property
def value(self):
"""
Based on the data type, access the actual value the instance stores
A little shorthand/quick way to get what is actually here
"""
if self.field.data_type == CustomField.FieldDataType.STRING:
return self.value_text
elif self.field.data_type == CustomField.FieldDataType.URL:
return self.value_url
elif self.field.data_type == CustomField.FieldDataType.DATE:
return self.value_date
elif self.field.data_type == CustomField.FieldDataType.BOOL:
return self.value_bool
elif self.field.data_type == CustomField.FieldDataType.INT:
return self.value_int
elif self.field.data_type == CustomField.FieldDataType.FLOAT:
return self.value_float
elif self.field.data_type == CustomField.FieldDataType.MONETARY:
return self.value_monetary
raise NotImplementedError(self.field.data_type)
if settings.AUDIT_LOG_ENABLED:
auditlog.register(Document, m2m_fields={"tags"})
auditlog.register(Correspondent)
auditlog.register(Tag)
auditlog.register(DocumentType)
auditlog.register(Note)
auditlog.register(CustomField)
auditlog.register(CustomFieldInstance)