diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 14ae6a17b..0e93c5dc0 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -13,8 +13,8 @@ from timezone_field import TimeZoneFormField from circuits.models import Circuit, CircuitTermination, Provider from extras.forms import ( - AddRemoveTagsForm, CustomFieldBulkEditForm, CustomFieldForm, CustomFieldModelCSVForm, CustomFieldFilterForm, - CustomFieldModelForm, LocalConfigContextFilterForm, + AddRemoveTagsForm, CustomFieldBulkEditForm, CustomFieldModelCSVForm, CustomFieldFilterForm, CustomFieldModelForm, + CustomFieldsMixin, LocalConfigContextFilterForm, ) from extras.models import Tag from ipam.constants import BGP_ASN_MAX, BGP_ASN_MIN @@ -2553,7 +2553,7 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt # Device components # -class ComponentCreateForm(BootstrapMixin, CustomFieldForm, ComponentForm): +class ComponentCreateForm(BootstrapMixin, CustomFieldsMixin, ComponentForm): """ Base form for the creation of device components (models subclassed from ComponentModel). """ @@ -2570,7 +2570,7 @@ class ComponentCreateForm(BootstrapMixin, CustomFieldForm, ComponentForm): ) -class DeviceBulkAddComponentForm(BootstrapMixin, CustomFieldForm, ComponentForm): +class DeviceBulkAddComponentForm(BootstrapMixin, CustomFieldsMixin, ComponentForm): pk = forms.ModelMultipleChoiceField( queryset=Device.objects.all(), widget=forms.MultipleHiddenInput() diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py index 671c2177c..1c36847dd 100644 --- a/netbox/extras/forms.py +++ b/netbox/extras/forms.py @@ -21,59 +21,58 @@ from .utils import FeatureQuery # Custom fields # -class CustomFieldForm(forms.Form): +class CustomFieldsMixin: """ - Extend Form to include custom field support. + Extend a Form to include custom field support. """ - model = None - def __init__(self, *args, **kwargs): - if self.model is None: - raise NotImplementedError("CustomFieldForm must specify a model class.") - self.custom_fields = [] - - super().__init__(*args, **kwargs) - - # Append relevant custom fields to the form instance - obj_type = ContentType.objects.get_for_model(self.model) - for cf in CustomField.objects.filter(content_types=obj_type): - field_name = 'cf_{}'.format(cf.name) - self.fields[field_name] = cf.to_form_field() - - # Annotate the field in the list of CustomField form fields - self.custom_fields.append(field_name) - - -class CustomFieldModelForm(forms.ModelForm): - """ - Extend ModelForm to include custom field support. - """ - - def __init__(self, *args, **kwargs): - - self.obj_type = ContentType.objects.get_for_model(self._meta.model) self.custom_fields = [] super().__init__(*args, **kwargs) self._append_customfield_fields() + def _get_content_type(self): + """ + Return the ContentType of the form's model. + """ + if not hasattr(self, 'model'): + raise NotImplementedError(f"{self.__class__.__name__} must specify a model class.") + return ContentType.objects.get_for_model(self.model) + + def _get_form_field(self, customfield): + return customfield.to_form_field() + def _append_customfield_fields(self): """ - Append form fields for all CustomFields assigned to this model. + Append form fields for all CustomFields assigned to this object type. """ + content_type = self._get_content_type() + # Append form fields; assign initial values if modifying and existing object - for cf in CustomField.objects.filter(content_types=self.obj_type): - field_name = 'cf_{}'.format(cf.name) - if self.instance.pk: - self.fields[field_name] = cf.to_form_field(set_initial=False) - self.fields[field_name].initial = self.instance.custom_field_data.get(cf.name) - else: - self.fields[field_name] = cf.to_form_field() + for customfield in CustomField.objects.filter(content_types=content_type): + field_name = f'cf_{customfield.name}' + self.fields[field_name] = self._get_form_field(customfield) # Annotate the field in the list of CustomField form fields self.custom_fields.append(field_name) + +class CustomFieldModelForm(CustomFieldsMixin, forms.ModelForm): + """ + Extend ModelForm to include custom field support. + """ + def _get_content_type(self): + return ContentType.objects.get_for_model(self._meta.model) + + def _get_form_field(self, customfield): + if self.instance.pk: + form_field = customfield.to_form_field(set_initial=False) + form_field.initial = self.instance.custom_field_data.get(customfield.name, None) + return form_field + + return customfield.to_form_field() + def clean(self): # Save custom field data on instance @@ -85,15 +84,8 @@ class CustomFieldModelForm(forms.ModelForm): class CustomFieldModelCSVForm(CSVModelForm, CustomFieldModelForm): - def _append_customfield_fields(self): - - # Append form fields - for cf in CustomField.objects.filter(content_types=self.obj_type): - field_name = 'cf_{}'.format(cf.name) - self.fields[field_name] = cf.to_form_field(for_csv_import=True) - - # Annotate the field in the list of CustomField form fields - self.custom_fields.append(field_name) + def _get_form_field(self, customfield): + return customfield.to_form_field(for_csv_import=True) class CustomFieldBulkEditForm(BulkEditForm):