diff --git a/netbox/dcim/forms/connections.py b/netbox/dcim/forms/connections.py index d3588da39..e71b65f20 100644 --- a/netbox/dcim/forms/connections.py +++ b/netbox/dcim/forms/connections.py @@ -14,6 +14,10 @@ def get_cable_form(a_type, b_type): def __new__(mcs, name, bases, attrs): + # NOTE: Cable.clone() mirrors the parent selector mapping below: + # termination_{end}_device / termination_{end}_powerpanel / termination_{end}_circuit + # This supports both the "Clone" and "Create & Add Another" workflows. + # If you change the mapping here, update Cable.clone() accordingly. for cable_end, term_cls in (('a', a_type), ('b', b_type)): # Device component diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index 9835e13f8..b55ddbc4c 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -304,6 +304,50 @@ class Cable(PrimaryModel): except UnsupportedCablePath as e: raise AbortRequest(e) + def clone(self): + """ + Return attributes suitable for cloning this cable. + + In addition to the fields defined in `clone_fields`, include the termination + type and parent selector fields used by dcim.forms.connections.get_cable_form(). + """ + attrs = super().clone() + + # Mirror dcim.forms.connections.get_cable_form() parent-field logic + for cable_end, terminations in (('a', self.a_terminations), ('b', self.b_terminations)): + if not terminations: + continue + + term_cls = type(terminations[0]) + term_label = term_cls._meta.label_lower + + # Matches CableForm choices: "." + attrs[f'{cable_end}_terminations_type'] = term_label + + # Device component + if hasattr(term_cls, 'device'): + device_ids = sorted({t.device_id for t in terminations if t.device_id}) + if device_ids: + attrs[f'termination_{cable_end}_device'] = device_ids + + # PowerFeed + elif term_label == 'dcim.powerfeed': + powerpanel_ids = sorted({t.power_panel_id for t in terminations if t.power_panel_id}) + if powerpanel_ids: + attrs[f'termination_{cable_end}_powerpanel'] = powerpanel_ids + + # CircuitTermination + elif term_label == 'circuits.circuittermination': + circuit_ids = sorted({t.circuit_id for t in terminations if t.circuit_id}) + if circuit_ids: + attrs[f'termination_{cable_end}_circuit'] = circuit_ids + + # Never clone the actual terminations, as they are already occupied + attrs.pop('a_terminations', None) + attrs.pop('b_terminations', None) + + return attrs + def serialize_object(self, exclude=None): data = serialize_object(self, exclude=exclude or []) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index d0497323d..8a1fa6bd9 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -3902,19 +3902,6 @@ class CableEditView(generic.ObjectEditView): return super().alter_object(obj, request, url_args, url_kwargs) - def get_extra_addanother_params(self, request): - - params = { - 'a_terminations_type': request.GET.get('a_terminations_type'), - 'b_terminations_type': request.GET.get('b_terminations_type') - } - - for key in request.POST: - if 'device' in key or 'power_panel' in key or 'circuit' in key: - params.update({key: request.POST.get(key)}) - - return params - @register_model_view(Cable, 'delete') class CableDeleteView(generic.ObjectDeleteView):