Repository: netbox-community/netbox

Issue:
Module Bulk Import: Server error page comes up if data is incorrect
### NetBox version

v3.4.1

### Python version

3.8

### Steps to Reproduce

1. Navigate to "Module Bulk Import" page (/dcim/modules/import/)
2. In the text box, add the following top line: "device,module_bay,module_type,status"
3. In the text box, add the following second line: "_non-device_,slot 2,Micro 2000,active"
4. Ensure the 'non-device' field does not match a device name that exists in NetBox
5. Click 'submit'
6. Observe how a 'Server Error' page comes up


![net](https://user-images.githubusercontent.com/25205963/210086744-e548d2d4-7f24-472d-8ad8-d9f6eddaab8c.PNG)
![net2](https://user-images.githubusercontent.com/25205963/210086754-3f2efdf4-bec7-44bf-9fa5-4ddeb567a8eb.PNG)


### Expected Behavior

If an issue exists with the inputted data, the page should direct the user to where the issue exists. In the provided example, the issue would be in row 1, column 1. This should be shown to the user to instead of failing. This would align it with other bulk import pages. This issue seems to exist with all fields that uses netbox stored data such as module_bay and module_type. 

Example from the device import page of expected behavior:

![net3](https://user-images.githubusercontent.com/25205963/210087392-3dbe8db7-cc64-4088-8bd5-32d681dca074.PNG)


### Observed Behavior

If inputted data is incorrect, a server error page pulls up instead of telling the user where the issue is. This leads to frustration on which line is causing the issue.


Discussion:

Resolving patch:
diff --git a/netbox/dcim/forms/common.py b/netbox/dcim/forms/common.py
--- a/netbox/dcim/forms/common.py
+++ b/netbox/dcim/forms/common.py
@@ -56,8 +56,8 @@ class ModuleCommonForm(forms.Form):
     def clean(self):
         super().clean()
 
-        replicate_components = self.cleaned_data.get("replicate_components")
-        adopt_components = self.cleaned_data.get("adopt_components")
+        replicate_components = self.cleaned_data.get('replicate_components')
+        adopt_components = self.cleaned_data.get('adopt_components')
         device = self.cleaned_data.get('device')
         module_type = self.cleaned_data.get('module_type')
         module_bay = self.cleaned_data.get('module_bay')
@@ -65,8 +65,9 @@ def clean(self):
         if adopt_components:
             self.instance._adopt_components = True
 
-        # Bail out if we are not installing a new module or if we are not replicating components
-        if self.instance.pk or not replicate_components:
+        # Bail out if we are not installing a new module or if we are not replicating components (or if
+        # validation has already failed)
+        if self.errors or self.instance.pk or not replicate_components:
             self.instance._disable_replication = True
             return
 
diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py
--- a/netbox/dcim/models/devices.py
+++ b/netbox/dcim/models/devices.py
@@ -961,7 +961,7 @@ def get_status_color(self):
     def clean(self):
         super().clean()
 
-        if self.module_bay.device != self.device:
+        if hasattr(self, "module_bay") and (self.module_bay.device != self.device):
             raise ValidationError(
                 f"Module must be installed within a module bay belonging to the assigned device ({self.device})."
             )


Test patch: