|
|
@@ -1,4 +1,5 @@
|
|
|
from collections import defaultdict
|
|
|
+from functools import cached_property
|
|
|
|
|
|
from django.contrib.contenttypes.fields import GenericRelation
|
|
|
from django.db.models.signals import class_prepared
|
|
|
@@ -133,18 +134,35 @@ class CustomFieldsMixin(models.Model):
|
|
|
class Meta:
|
|
|
abstract = True
|
|
|
|
|
|
- @property
|
|
|
+ @cached_property
|
|
|
def cf(self):
|
|
|
"""
|
|
|
- A pass-through convenience alias for accessing `custom_field_data` (read-only).
|
|
|
+ Return a dictionary mapping each custom field for this instance to its deserialized value.
|
|
|
|
|
|
```python
|
|
|
>>> tenant = Tenant.objects.first()
|
|
|
>>> tenant.cf
|
|
|
- {'cust_id': 'CYB01'}
|
|
|
+ {'primary_site': <Site: DM-NYC>, 'cust_id': 'DMI01', 'is_active': True}
|
|
|
```
|
|
|
"""
|
|
|
- return self.custom_field_data
|
|
|
+ return {
|
|
|
+ cf.name: cf.deserialize(self.custom_field_data.get(cf.name))
|
|
|
+ for cf in self.custom_fields
|
|
|
+ }
|
|
|
+
|
|
|
+ @cached_property
|
|
|
+ def custom_fields(self):
|
|
|
+ """
|
|
|
+ Return the QuerySet of CustomFields assigned to this model.
|
|
|
+
|
|
|
+ ```python
|
|
|
+ >>> tenant = Tenant.objects.first()
|
|
|
+ >>> tenant.custom_fields
|
|
|
+ <RestrictedQuerySet [<CustomField: Primary site>, <CustomField: Customer ID>, <CustomField: Is active>]>
|
|
|
+ ```
|
|
|
+ """
|
|
|
+ from extras.models import CustomField
|
|
|
+ return CustomField.objects.get_for_model(self)
|
|
|
|
|
|
def get_custom_fields(self, omit_hidden=False):
|
|
|
"""
|
|
|
@@ -155,10 +173,13 @@ class CustomFieldsMixin(models.Model):
|
|
|
>>> tenant.get_custom_fields()
|
|
|
{<CustomField: Customer ID>: 'CYB01'}
|
|
|
```
|
|
|
+
|
|
|
+ Args:
|
|
|
+ omit_hidden: If True, custom fields with no UI visibility will be omitted.
|
|
|
"""
|
|
|
from extras.models import CustomField
|
|
|
-
|
|
|
data = {}
|
|
|
+
|
|
|
for field in CustomField.objects.get_for_model(self):
|
|
|
# Skip fields that are hidden if 'omit_hidden' is set
|
|
|
if omit_hidden and field.ui_visibility == CustomFieldVisibilityChoices.VISIBILITY_HIDDEN:
|
|
|
@@ -172,12 +193,28 @@ class CustomFieldsMixin(models.Model):
|
|
|
def get_custom_fields_by_group(self):
|
|
|
"""
|
|
|
Return a dictionary of custom field/value mappings organized by group. Hidden fields are omitted.
|
|
|
+
|
|
|
+ ```python
|
|
|
+ >>> tenant = Tenant.objects.first()
|
|
|
+ >>> tenant.get_custom_fields_by_group()
|
|
|
+ {
|
|
|
+ '': {<CustomField: Primary site>: <Site: DM-NYC>},
|
|
|
+ 'Billing': {<CustomField: Customer ID>: 'DMI01', <CustomField: Is active>: True}
|
|
|
+ }
|
|
|
+ ```
|
|
|
"""
|
|
|
- grouped_custom_fields = defaultdict(dict)
|
|
|
- for cf, value in self.get_custom_fields(omit_hidden=True).items():
|
|
|
- grouped_custom_fields[cf.group_name][cf] = value
|
|
|
+ from extras.models import CustomField
|
|
|
+ groups = defaultdict(dict)
|
|
|
+ visible_custom_fields = CustomField.objects.get_for_model(self).exclude(
|
|
|
+ ui_visibility=CustomFieldVisibilityChoices.VISIBILITY_HIDDEN
|
|
|
+ )
|
|
|
+
|
|
|
+ for cf in visible_custom_fields:
|
|
|
+ value = self.custom_field_data.get(cf.name)
|
|
|
+ value = cf.deserialize(value)
|
|
|
+ groups[cf.group_name][cf] = value
|
|
|
|
|
|
- return dict(grouped_custom_fields)
|
|
|
+ return dict(groups)
|
|
|
|
|
|
def clean(self):
|
|
|
super().clean()
|