Skip to content

Commit 25b8a0f

Browse files
committed
Add CustomObjectContentTypeChoiceField and object field filtering in edit form
1 parent 6ead766 commit 25b8a0f

File tree

2 files changed

+72
-7
lines changed

2 files changed

+72
-7
lines changed

netbox_custom_objects/forms.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from extras.forms import CustomFieldForm
1414
from utilities.forms.fields import CommentField, ContentTypeChoiceField, DynamicModelChoiceField
1515
from utilities.forms.rendering import FieldSet
16+
from utilities.string import title
1617

1718
__all__ = (
1819
'CustomObjectTypeForm',
@@ -43,6 +44,46 @@ class Meta:
4344
# fields = ('name', 'label', 'custom_object_type', 'field_type',)
4445

4546

47+
class CustomObjectContentTypeChoiceField(ContentTypeChoiceField):
48+
"""
49+
Selection field for a single content type.
50+
"""
51+
custom_object_types = None
52+
53+
def __init__(self, queryset, *args, **kwargs):
54+
# Order ContentTypes by app_label
55+
queryset = queryset.order_by('app_label', 'model')
56+
self.custom_object_types = CustomObjectType.objects.all()
57+
super().__init__(queryset, *args, **kwargs)
58+
59+
def object_type_name(self, object_type, include_app=True):
60+
"""
61+
Return a human-friendly ObjectType name (e.g. "DCIM > Site").
62+
"""
63+
try:
64+
meta = object_type.model_class()._meta
65+
app_label = title(meta.app_config.verbose_name)
66+
model_name = title(meta.verbose_name)
67+
if include_app:
68+
return f'{app_label} > {model_name}'
69+
return model_name
70+
except AttributeError:
71+
if object_type.app_label == 'netbox_custom_objects':
72+
try:
73+
model_name = self.custom_object_types.get(slug=object_type.model).name
74+
return f'Custom Objects > {model_name} (Custom)'
75+
except CustomObjectType.DoesNotExist:
76+
pass
77+
# Model does not exist
78+
return f'{object_type.app_label} > {object_type.model}'
79+
80+
def label_from_instance(self, obj):
81+
try:
82+
return self.object_type_name(obj)
83+
except AttributeError:
84+
return super().label_from_instance(obj)
85+
86+
4687
class CustomObjectTypeFieldForm(CustomFieldForm):
4788
# This field should be removed or at least "required" should be defeated
4889
object_types = forms.CharField(
@@ -55,7 +96,7 @@ class CustomObjectTypeFieldForm(CustomFieldForm):
5596
required=True,
5697
label=_('Custom object type')
5798
)
58-
related_object_type = ContentTypeChoiceField(
99+
related_object_type = CustomObjectContentTypeChoiceField(
59100
label=_('Related object type'),
60101
queryset=CustomObjectObjectType.objects.public(),
61102
# choices=[("foo", "bar")],

netbox_custom_objects/models.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ def __str__(self):
412412

413413
@property
414414
def model_class(self):
415+
if self.related_object_type.app_label == 'netbox_custom_objects':
416+
return apps.get_model(self.related_object_type.app_label, 'customobjecttype')
415417
return apps.get_model(self.related_object_type.app_label, self.related_object_type.model)
416418

417419
@property
@@ -626,6 +628,26 @@ def deserialize(self, value):
626628
return model.objects.filter(pk__in=value)
627629
return value
628630

631+
@property
632+
def related_custom_object_type(self):
633+
if self.related_object_type.app_label != 'netbox_custom_objects':
634+
return None
635+
return CustomObjectType.objects.get(slug=self.related_object_type.model)
636+
637+
@property
638+
def related_object_queryset(self):
639+
if model_class := self.related_object_type.model_class():
640+
return model_class.objects.all()
641+
return CustomObject.objects.filter(custom_object_type=self.related_custom_object_type)
642+
643+
@property
644+
def related_custom_object_type_filter(self):
645+
if self.related_object_type.app_label != 'netbox_custom_objects':
646+
return {}
647+
return {
648+
'custom_object_type': self.related_custom_object_type.id,
649+
}
650+
629651
def to_form_field(self, set_initial=True, enforce_required=True, enforce_visibility=True, for_csv_import=False):
630652
"""
631653
Return a form field suitable for setting a CustomField's value for an object.
@@ -719,30 +741,32 @@ def to_form_field(self, set_initial=True, enforce_required=True, enforce_visibil
719741

720742
# Object
721743
elif self.type == CustomFieldTypeChoices.TYPE_OBJECT:
722-
model = self.related_object_type.model_class()
723744
field_class = CSVModelChoiceField if for_csv_import else DynamicModelChoiceField
724745
kwargs = {
725-
'queryset': model.objects.all(),
746+
'queryset': self.related_object_queryset,
726747
'required': required,
727748
'initial': initial,
728749
}
729750
if not for_csv_import:
730-
kwargs['query_params'] = self.related_object_filter
751+
query_params = self.related_object_filter or {}
752+
query_params.update(self.related_custom_object_type_filter)
753+
kwargs['query_params'] = query_params
731754
kwargs['selector'] = True
732755

733756
field = field_class(**kwargs)
734757

735758
# Multiple objects
736759
elif self.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT:
737-
model = self.related_object_type.model_class()
738760
field_class = CSVModelMultipleChoiceField if for_csv_import else DynamicModelMultipleChoiceField
739761
kwargs = {
740-
'queryset': model.objects.all(),
762+
'queryset': self.related_object_queryset,
741763
'required': required,
742764
'initial': initial,
743765
}
744766
if not for_csv_import:
745-
kwargs['query_params'] = self.related_object_filter
767+
query_params = self.related_object_filter or {}
768+
query_params.update(self.related_custom_object_type_filter)
769+
kwargs['query_params'] = query_params
746770
kwargs['selector'] = True
747771

748772
field = field_class(**kwargs)

0 commit comments

Comments
 (0)