From b2e05aafc1a99d14fea2ab9c446a8dd7b4cd6375 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 28 Dec 2020 12:54:42 -0500 Subject: [PATCH] Closes #5531: Ensure consistent calls to parent clean() methods for models, forms --- netbox/dcim/forms.py | 5 +++++ netbox/dcim/models/device_component_templates.py | 2 ++ netbox/dcim/models/device_components.py | 6 ++++++ netbox/dcim/models/racks.py | 1 + netbox/extras/models/customfields.py | 4 ++++ netbox/extras/models/models.py | 6 +++++- netbox/users/admin.py | 2 ++ netbox/utilities/forms/forms.py | 2 ++ netbox/virtualization/models.py | 1 + 9 files changed, 28 insertions(+), 1 deletion(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 4cc1a532b..b75f9ba9a 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -134,6 +134,7 @@ class ComponentForm(BootstrapMixin, forms.Form): ) def clean(self): + super().clean() # Validate that the number of components being created from both the name_pattern and label_pattern are equal if self.cleaned_data['label_pattern']: @@ -1438,6 +1439,7 @@ class FrontPortTemplateCreateForm(ComponentTemplateCreateForm): self.fields['rear_port_set'].choices = choices def clean(self): + super().clean() # Validate that the number of ports being created equals the number of selected (rear port, position) tuples front_port_count = len(self.cleaned_data['name_pattern']) @@ -2929,6 +2931,7 @@ class InterfaceBulkEditForm( self.fields['lag'].widget.attrs['disabled'] = True def clean(self): + super().clean() # Untagged interfaces cannot be assigned tagged VLANs if self.cleaned_data['mode'] == InterfaceModeChoices.MODE_ACCESS and self.cleaned_data['tagged_vlans']: @@ -3077,6 +3080,7 @@ class FrontPortCreateForm(ComponentCreateForm): self.fields['rear_port_set'].choices = choices def clean(self): + super().clean() # Validate that the number of ports being created equals the number of selected (rear port, position) tuples front_port_count = len(self.cleaned_data['name_pattern']) @@ -3909,6 +3913,7 @@ class CableBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm): ] def clean(self): + super().clean() # Validate length/unit length = self.cleaned_data.get('length') diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index 7a94b3e1b..fe819d310 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -193,6 +193,7 @@ class PowerOutletTemplate(ComponentTemplateModel): unique_together = ('device_type', 'name') def clean(self): + super().clean() # Validate power port assignment if self.power_port and self.power_port.device_type != self.device_type: @@ -278,6 +279,7 @@ class FrontPortTemplate(ComponentTemplateModel): ) def clean(self): + super().clean() # Validate rear port assignment if self.rear_port.device_type != self.device_type: diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 5d5825f5f..21ca92da8 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -316,6 +316,7 @@ class PowerPort(CableTermination, PathEndpoint, ComponentModel): ) def clean(self): + super().clean() if self.maximum_draw is not None and self.allocated_draw is not None: if self.allocated_draw > self.maximum_draw: @@ -425,6 +426,7 @@ class PowerOutlet(CableTermination, PathEndpoint, ComponentModel): ) def clean(self): + super().clean() # Validate power port assignment if self.power_port and self.power_port.device != self.device: @@ -555,6 +557,7 @@ class Interface(CableTermination, PathEndpoint, ComponentModel, BaseInterface): ) def clean(self): + super().clean() # Virtual interfaces cannot be connected if self.type in NONCONNECTABLE_IFACE_TYPES and ( @@ -668,6 +671,7 @@ class FrontPort(CableTermination, ComponentModel): ) def clean(self): + super().clean() # Validate rear port assignment if self.rear_port.device != self.device: @@ -711,6 +715,7 @@ class RearPort(CableTermination, ComponentModel): return reverse('dcim:rearport', kwargs={'pk': self.pk}) def clean(self): + super().clean() # Check that positions count is greater than or equal to the number of associated FrontPorts frontport_count = self.frontports.count() @@ -768,6 +773,7 @@ class DeviceBay(ComponentModel): ) def clean(self): + super().clean() # Validate that the parent Device can have DeviceBays if not self.device.device_type.is_parent_device: diff --git a/netbox/dcim/models/racks.py b/netbox/dcim/models/racks.py index de9021f35..ccc775954 100644 --- a/netbox/dcim/models/racks.py +++ b/netbox/dcim/models/racks.py @@ -109,6 +109,7 @@ class RackGroup(MPTTModel, ChangeLoggedModel): ) def clean(self): + super().clean() # Parent RackGroup (if any) must belong to the same Site if self.parent and self.parent.site != self.site: diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index 6f4c5f9e1..693158bc5 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -47,6 +47,8 @@ class CustomFieldModel(models.Model): ]) def clean(self): + super().clean() + custom_fields = {cf.name: cf for cf in CustomField.objects.get_for_model(self)} # Validate all field values @@ -172,6 +174,8 @@ class CustomField(models.Model): obj.save() def clean(self): + super().clean() + # Validate the field's default value (if any) if self.default is not None: try: diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index 8934d9732..4917a7e44 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -117,11 +117,15 @@ class Webhook(models.Model): return self.name def clean(self): + super().clean() + + # At least one action type must be selected if not self.type_create and not self.type_delete and not self.type_update: raise ValidationError( "You must select at least one type: create, update, and/or delete." ) + # CA file path requires SSL verification enabled if not self.ssl_verification and self.ca_file_path: raise ValidationError({ 'ca_file_path': 'Do not specify a CA certificate file if SSL verification is disabled.' @@ -436,6 +440,7 @@ class ConfigContext(ChangeLoggedModel): return reverse('extras:configcontext', kwargs={'pk': self.pk}) def clean(self): + super().clean() # Verify that JSON data is provided as an object if type(self.data) is not dict: @@ -482,7 +487,6 @@ class ConfigContextModel(models.Model): return data def clean(self): - super().clean() # Verify that JSON data is provided as an object diff --git a/netbox/users/admin.py b/netbox/users/admin.py index c03e6f740..f2fe4a0b4 100644 --- a/netbox/users/admin.py +++ b/netbox/users/admin.py @@ -169,6 +169,8 @@ class ObjectPermissionForm(forms.ModelForm): self.instance.actions.remove(action) def clean(self): + super().clean() + object_types = self.cleaned_data.get('object_types') constraints = self.cleaned_data.get('constraints') diff --git a/netbox/utilities/forms/forms.py b/netbox/utilities/forms/forms.py index f259f6b6d..e674afdf7 100644 --- a/netbox/utilities/forms/forms.py +++ b/netbox/utilities/forms/forms.py @@ -82,6 +82,7 @@ class BulkRenameForm(forms.Form): ) def clean(self): + super().clean() # Validate regular expression in "find" field if self.cleaned_data['use_regex']: @@ -124,6 +125,7 @@ class ImportForm(BootstrapMixin, forms.Form): ) def clean(self): + super().clean() data = self.cleaned_data['data'] format = self.cleaned_data['format'] diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py index cf7737340..edca7e1fe 100644 --- a/netbox/virtualization/models.py +++ b/netbox/virtualization/models.py @@ -444,6 +444,7 @@ class VMInterface(BaseInterface): ) def clean(self): + super().clean() # Validate untagged VLAN if self.untagged_vlan and self.untagged_vlan.site not in [self.virtual_machine.site, None]: