mirror of
https://github.com/netbox-community/netbox.git
synced 2026-04-23 09:18:49 +02:00
feat(dcim): Add Cable cloning with Termination mapping
Introduce `clone()` method for the Cable model to enable cloning its attributes, including termination type and parent selectors. Updates mappings to align with CableForm workflows, supporting "Clone" and "Create & Add Another" actions. Fixes #21429
This commit is contained in:
@@ -14,6 +14,10 @@ def get_cable_form(a_type, b_type):
|
|||||||
|
|
||||||
def __new__(mcs, name, bases, attrs):
|
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)):
|
for cable_end, term_cls in (('a', a_type), ('b', b_type)):
|
||||||
|
|
||||||
# Device component
|
# Device component
|
||||||
|
|||||||
@@ -304,6 +304,50 @@ class Cable(PrimaryModel):
|
|||||||
except UnsupportedCablePath as e:
|
except UnsupportedCablePath as e:
|
||||||
raise AbortRequest(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: "<app_label>.<model>"
|
||||||
|
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):
|
def serialize_object(self, exclude=None):
|
||||||
data = serialize_object(self, exclude=exclude or [])
|
data = serialize_object(self, exclude=exclude or [])
|
||||||
|
|
||||||
|
|||||||
@@ -3902,19 +3902,6 @@ class CableEditView(generic.ObjectEditView):
|
|||||||
|
|
||||||
return super().alter_object(obj, request, url_args, url_kwargs)
|
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')
|
@register_model_view(Cable, 'delete')
|
||||||
class CableDeleteView(generic.ObjectDeleteView):
|
class CableDeleteView(generic.ObjectDeleteView):
|
||||||
|
|||||||
Reference in New Issue
Block a user