Repository: netbox-community/netbox

Issue:
Make connecting cables quicker by pre-selecting B end Site and Rack
### Environment
* Python version:  3.5.2
* NetBox version:  2.6.1

### Proposed Functionality

When creating a new cable, pre-select the Site and Rack of the B-end to be the Site and Rack of the A-end.

Anybody who wants to connect elsewhere would still have to select the correct Rack or Site/Rack anyway, so you might as well pre-select the most likely choice.

### Use Case

In 2.6.1 there has been a regression in the UI, which means that connecting cables has become more tiresome.

After a data center move, I now have hundreds of cables to connect up.  CSV import is a possibility, but due to heterogenous devices and interface names, selecting them from the available interfaces in the UI would make more sense.

However, the interface for adding a cable has changed between 2.5 and 2.6.  Previously there were two tabs for the B end: under the first tab, you could just type the name of any device (or part of it) to select it.  Under the second tab, you could choose to drill down via site and rack.

It appears the first option has been removed.  Now what happens is:

1. You click on the "Connect" button and you get a drop-down.  I know I'm connecting to an Interface, so that's OK.

    ![image](https://user-images.githubusercontent.com/44789/60384113-c6467900-9a71-11e9-8e6f-714b81ea9459.png)

2. At the B side you must drill down via site, rack and device.

    ![image](https://user-images.githubusercontent.com/44789/60383941-158baa00-9a70-11e9-8d5d-c27461873e24.png)

3. If you try to type into the "device" box without selecting Site/Rack, you are only allowed to choose *unracked* devices (across all sites).  So you are forced to select Site and Rack (or just Rack) first, before typing in part of the target device name.

Under 2.5 it was going to be simple - working through the devices in rack X, I would have the name of the switch in rack X in my clipboard and just paste it in to the target device, then just have to select the interface. I can't do this now, without also selecting Site/Rack every time.

There are a bunch of ways the UI for connecting devices could be improved, but pre-selecting the Site and Rack would be minimally invasive and yet speed up the 80/20 case where connections are made within a rack (e.g. device to top-of-rack switch).

Another option would be that if the site and rack are unselecting, then typing into the Device field could search across all devices in the database.  However I understand that is going to become less feasible when naming uniqueness rules are relaxed.

### Database Changes

None

### External Dependencies

None


Discussion:
For me, pre-populating the rack wouldn't be useful, but the site would be fine. The bigger thing for me would be to be able to search all of my devices without having to first select the site/rack. (my patch panels, for example, are named <building>-<type>-patch.

Resolving patch:
diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py
--- a/netbox/dcim/forms.py
+++ b/netbox/dcim/forms.py
@@ -3168,6 +3168,11 @@ class Meta:
             'termination_b_site', 'termination_b_rack', 'termination_b_device', 'termination_b_id', 'type', 'status',
             'label', 'color', 'length', 'length_unit',
         ]
+        widgets = {
+            'status': StaticSelect2,
+            'type': StaticSelect2,
+            'length_unit': StaticSelect2,
+        }
 
 
 class ConnectCableToConsolePortForm(ConnectCableToDeviceForm):
@@ -3363,6 +3368,11 @@ class Meta:
         fields = [
             'type', 'status', 'label', 'color', 'length', 'length_unit',
         ]
+        widgets = {
+            'status': StaticSelect2,
+            'type': StaticSelect2,
+            'length_unit': StaticSelect2,
+        }
 
 
 class CableCSVForm(forms.ModelForm):
diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py
--- a/netbox/dcim/views.py
+++ b/netbox/dcim/views.py
@@ -1945,6 +1945,12 @@ def get(self, request, *args, **kwargs):
         # Parse initial data manually to avoid setting field values as lists
         initial_data = {k: request.GET[k] for k in request.GET}
 
+        # Set initial site and rack based on side A termination (if not already set)
+        if 'termination_b_site' not in initial_data:
+            initial_data['termination_b_site'] = getattr(self.obj.termination_a.parent, 'site', None)
+        if 'termination_b_rack' not in initial_data:
+            initial_data['termination_b_rack'] = getattr(self.obj.termination_a.parent, 'rack', None)
+
         form = self.form_class(instance=self.obj, initial=initial_data)
 
         return render(request, self.template_name, {


Test patch: