initial work on #5952

This commit is contained in:
hellerve 2021-04-08 13:55:33 +02:00
parent ae3527df16
commit 859bda079e
3 changed files with 65 additions and 21 deletions

View File

@ -59,6 +59,17 @@ POWERFEED_MAX_UTILIZATION_DEFAULT = 80 # Percentage
# Cabling and connections # Cabling and connections
# #
# Cable endpoint parent types
CABLE_TERMINATION_PARENT_MODELS = Q(
Q(app_label='circuits', model__in=(
'circuit',
)) |
Q(app_label='dcim', model__in=(
'device',
'powerpanel',
))
)
# Cable endpoint types # Cable endpoint types
CABLE_TERMINATION_MODELS = Q( CABLE_TERMINATION_MODELS = Q(
Q(app_label='circuits', model__in=( Q(app_label='circuits', model__in=(

View File

@ -3819,10 +3819,13 @@ class CableForm(BootstrapMixin, CustomFieldModelForm):
class CableCSVForm(CustomFieldModelCSVForm): class CableCSVForm(CustomFieldModelCSVForm):
# Termination A # Termination A
side_a_device = CSVModelChoiceField( side_a_parent_type = CSVContentTypeField(
queryset=Device.objects.all(), queryset=ContentType.objects.all(),
to_field_name='name', limit_choices_to=CABLE_TERMINATION_PARENT_MODELS,
help_text='Side A device' help_text='Side A parent type'
)
side_a_parent_id = forms.IntegerField(
help_text='Side A parent ID'
) )
side_a_type = CSVContentTypeField( side_a_type = CSVContentTypeField(
queryset=ContentType.objects.all(), queryset=ContentType.objects.all(),
@ -3834,10 +3837,13 @@ class CableCSVForm(CustomFieldModelCSVForm):
) )
# Termination B # Termination B
side_b_device = CSVModelChoiceField( side_b_parent_type = CSVContentTypeField(
queryset=Device.objects.all(), queryset=ContentType.objects.all(),
to_field_name='name', limit_choices_to=CABLE_TERMINATION_PARENT_MODELS,
help_text='Side B device' help_text='Side B parent type'
)
side_b_parent_id = forms.IntegerField(
help_text='Side B parent ID'
) )
side_b_type = CSVContentTypeField( side_b_type = CSVContentTypeField(
queryset=ContentType.objects.all(), queryset=ContentType.objects.all(),
@ -3868,13 +3874,30 @@ class CableCSVForm(CustomFieldModelCSVForm):
class Meta: class Meta:
model = Cable model = Cable
fields = [ fields = [
'side_a_device', 'side_a_type', 'side_a_name', 'side_b_device', 'side_b_type', 'side_b_name', 'type', 'side_a_parent_type', 'side_a_parent_id', 'side_a_type', 'side_a_name',
'status', 'label', 'color', 'length', 'length_unit', 'side_b_parent_type', 'side_b_parent_id', 'side_b_type', 'side_b_name',
'type', 'status', 'label', 'color', 'length', 'length_unit',
] ]
help_texts = { help_texts = {
'color': mark_safe('RGB color in hexadecimal (e.g. <code>00ff00</code>)'), 'color': mark_safe('RGB color in hexadecimal (e.g. <code>00ff00</code>)'),
} }
def _get_parent(self, side):
parent_type = self.cleaned_data.get(f'side_{side}_parent_type')
parent_id = self.cleaned_data.get(f'side_{side}_parent_id')
if not parent_type or not parent_id:
return None
model = parent_type.model_class()
return model.objects.get(pk=parent_id)
def _translate_model(self, parent_type):
# TODO: maybe we went overboard with making this generic/extensible?
return {
"powerpanel": "power_panel",
}.get(parent_type.model, parent_type.model)
def _clean_side(self, side): def _clean_side(self, side):
""" """
Derive a Cable's A/B termination objects. Derive a Cable's A/B termination objects.
@ -3883,19 +3906,24 @@ class CableCSVForm(CustomFieldModelCSVForm):
""" """
assert side in 'ab', f"Invalid side designation: {side}" assert side in 'ab', f"Invalid side designation: {side}"
device = self.cleaned_data.get(f'side_{side}_device') parent_type = self.cleaned_data.get(f'side_{side}_parent_type')
parent = self._get_parent(side)
content_type = self.cleaned_data.get(f'side_{side}_type') content_type = self.cleaned_data.get(f'side_{side}_type')
name = self.cleaned_data.get(f'side_{side}_name') name = self.cleaned_data.get(f'side_{side}_name')
if not device or not content_type or not name: if not parent or not content_type or not name:
return None return None
model = content_type.model_class() model = content_type.model_class()
try: try:
termination_object = model.objects.get(device=device, name=name) # the parent is named like the model, we utilize that fact for the query
termination_object = model.objects.get(**{
"name" if hasattr(model, "name") else "pk": name,
self._translate_model(parent_type): parent
})
if termination_object.cable is not None: if termination_object.cable is not None:
raise forms.ValidationError(f"Side {side.upper()}: {device} {termination_object} is already connected") raise forms.ValidationError(f"Side {side.upper()}: {parent} {termination_object} is already connected")
except ObjectDoesNotExist: except ObjectDoesNotExist:
raise forms.ValidationError(f"{side.upper()} side termination not found: {device} {name}") raise forms.ValidationError(f"{side.upper()} side termination not found: {parent} {name}")
setattr(self.instance, f'termination_{side}', termination_object) setattr(self.instance, f'termination_{side}', termination_object)
return termination_object return termination_object

View File

@ -112,8 +112,9 @@ class Cable(ChangeLoggedModel, CustomFieldModel):
objects = RestrictedQuerySet.as_manager() objects = RestrictedQuerySet.as_manager()
csv_headers = [ csv_headers = [
'termination_a_type', 'termination_a_id', 'termination_b_type', 'termination_b_id', 'type', 'status', 'label', 'side_a_parent_type', 'side_a_parent_id', 'side_a_type', 'side_a_name',
'color', 'length', 'length_unit', 'side_b_parent_type', 'side_b_parent_id', 'side_b_type', 'side_b_name',
'type', 'status', 'label', 'color', 'length', 'length_unit',
] ]
class Meta: class Meta:
@ -281,12 +282,16 @@ class Cable(ChangeLoggedModel, CustomFieldModel):
def to_csv(self): def to_csv(self):
return ( return (
'{}.{}'.format(self.termination_a.parent._meta.app_label, self.termination_a.parent._meta.model_name),
self.termination_a.parent.id,
'{}.{}'.format(self.termination_a_type.app_label, self.termination_a_type.model), '{}.{}'.format(self.termination_a_type.app_label, self.termination_a_type.model),
self.termination_a_id, self.termination_a.name if hasattr(self.termination_a, "name") else self.termination_a_id,
'{}.{}'.format(self.termination_b.parent._meta.app_label, self.termination_b.parent._meta.model_name),
self.termination_b.parent.id,
'{}.{}'.format(self.termination_b_type.app_label, self.termination_b_type.model), '{}.{}'.format(self.termination_b_type.app_label, self.termination_b_type.model),
self.termination_b_id, self.termination_b.name if hasattr(self.termination_b, "name") else self.termination_b_id,
self.get_type_display(), self.type,
self.get_status_display(), self.status,
self.label, self.label,
self.color, self.color,
self.length, self.length,