mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-19 11:52:22 -06:00
Add PortAssignmentTemplate for device types
This commit is contained in:
@@ -884,13 +884,14 @@ class FrontPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeCo
|
|||||||
choices=PortTypeChoices,
|
choices=PortTypeChoices,
|
||||||
null_value=None
|
null_value=None
|
||||||
)
|
)
|
||||||
rear_port_id = django_filters.ModelMultipleChoiceFilter(
|
# TODO
|
||||||
queryset=RearPort.objects.all()
|
# rear_port_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
)
|
# queryset=RearPortTemplate.objects.all()
|
||||||
|
# )
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPortTemplate
|
model = FrontPortTemplate
|
||||||
fields = ('id', 'name', 'label', 'type', 'color', 'rear_port_position', 'description')
|
fields = ('id', 'name', 'label', 'type', 'color', 'positions', 'description')
|
||||||
|
|
||||||
|
|
||||||
class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
|
class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
|
||||||
@@ -898,6 +899,10 @@ class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeCom
|
|||||||
choices=PortTypeChoices,
|
choices=PortTypeChoices,
|
||||||
null_value=None
|
null_value=None
|
||||||
)
|
)
|
||||||
|
# TODO
|
||||||
|
# front_port_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
# queryset=FrontPortTemplate.objects.all()
|
||||||
|
# )
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RearPortTemplate
|
model = RearPortTemplate
|
||||||
|
|||||||
@@ -1112,14 +1112,10 @@ class InterfaceTemplateForm(ModularComponentTemplateForm):
|
|||||||
|
|
||||||
|
|
||||||
class FrontPortTemplateForm(ModularComponentTemplateForm):
|
class FrontPortTemplateForm(ModularComponentTemplateForm):
|
||||||
rear_port = DynamicModelChoiceField(
|
rear_ports = forms.MultipleChoiceField(
|
||||||
label=_('Rear port'),
|
choices=[],
|
||||||
queryset=RearPortTemplate.objects.all(),
|
label=_('Rear ports'),
|
||||||
required=False,
|
widget=forms.SelectMultiple(attrs={'size': 8})
|
||||||
query_params={
|
|
||||||
'device_type_id': '$device_type',
|
|
||||||
'module_type_id': '$module_type',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
@@ -1128,15 +1124,14 @@ class FrontPortTemplateForm(ModularComponentTemplateForm):
|
|||||||
FieldSet('device_type', name=_('Device Type')),
|
FieldSet('device_type', name=_('Device Type')),
|
||||||
FieldSet('module_type', name=_('Module Type')),
|
FieldSet('module_type', name=_('Module Type')),
|
||||||
),
|
),
|
||||||
'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'description',
|
'name', 'label', 'type', 'color', 'positions', 'rear_ports', 'description',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPortTemplate
|
model = FrontPortTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'device_type', 'module_type', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position',
|
'device_type', 'module_type', 'name', 'label', 'type', 'color', 'positions', 'description',
|
||||||
'description',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -1581,7 +1576,7 @@ class FrontPortForm(ModularDeviceComponentForm):
|
|||||||
rear_ports = forms.MultipleChoiceField(
|
rear_ports = forms.MultipleChoiceField(
|
||||||
choices=[],
|
choices=[],
|
||||||
label=_('Rear ports'),
|
label=_('Rear ports'),
|
||||||
widget=forms.SelectMultiple(attrs={'size': 6})
|
widget=forms.SelectMultiple(attrs={'size': 8})
|
||||||
)
|
)
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
|
|||||||
@@ -113,31 +113,11 @@ class FrontPortTemplateImportForm(forms.ModelForm):
|
|||||||
label=_('Type'),
|
label=_('Type'),
|
||||||
choices=PortTypeChoices.CHOICES
|
choices=PortTypeChoices.CHOICES
|
||||||
)
|
)
|
||||||
rear_port = forms.ModelChoiceField(
|
|
||||||
label=_('Rear port'),
|
|
||||||
queryset=RearPortTemplate.objects.all(),
|
|
||||||
to_field_name='name'
|
|
||||||
)
|
|
||||||
|
|
||||||
def clean_device_type(self):
|
|
||||||
if device_type := self.cleaned_data['device_type']:
|
|
||||||
rear_port = self.fields['rear_port']
|
|
||||||
rear_port.queryset = rear_port.queryset.filter(device_type=device_type)
|
|
||||||
|
|
||||||
return device_type
|
|
||||||
|
|
||||||
def clean_module_type(self):
|
|
||||||
if module_type := self.cleaned_data['module_type']:
|
|
||||||
rear_port = self.fields['rear_port']
|
|
||||||
rear_port.queryset = rear_port.queryset.filter(module_type=module_type)
|
|
||||||
|
|
||||||
return module_type
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPortTemplate
|
model = FrontPortTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'device_type', 'module_type', 'name', 'type', 'color', 'rear_port', 'rear_port_position', 'label',
|
'device_type', 'module_type', 'name', 'type', 'color', 'positions', 'label', 'description',
|
||||||
'description',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,34 @@ from itertools import islice
|
|||||||
|
|
||||||
|
|
||||||
def chunked(iterable, size):
|
def chunked(iterable, size):
|
||||||
"""Yield successive chunks of a given size from an iterator."""
|
"""
|
||||||
|
Yield successive chunks of a given size from an iterator.
|
||||||
|
"""
|
||||||
iterator = iter(iterable)
|
iterator = iter(iterable)
|
||||||
while chunk := list(islice(iterator, size)):
|
while chunk := list(islice(iterator, size)):
|
||||||
yield chunk
|
yield chunk
|
||||||
|
|
||||||
|
|
||||||
|
def populate_port_template_assignments(apps, schema_editor):
|
||||||
|
FrontPortTemplate = apps.get_model('dcim', 'FrontPortTemplate')
|
||||||
|
PortAssignmentTemplate = apps.get_model('dcim', 'PortAssignmentTemplate')
|
||||||
|
|
||||||
|
front_ports = FrontPortTemplate.objects.iterator(chunk_size=1000)
|
||||||
|
|
||||||
|
def generate_copies():
|
||||||
|
for front_port in front_ports:
|
||||||
|
yield PortAssignmentTemplate(
|
||||||
|
front_port_id=front_port.pk,
|
||||||
|
front_port_position=None,
|
||||||
|
rear_port_id=front_port.rear_port_id,
|
||||||
|
rear_port_position=front_port.rear_port_position,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Bulk insert in streaming batches
|
||||||
|
for chunk in chunked(generate_copies(), 1000):
|
||||||
|
PortAssignmentTemplate.objects.bulk_create(chunk, batch_size=1000)
|
||||||
|
|
||||||
|
|
||||||
def populate_port_assignments(apps, schema_editor):
|
def populate_port_assignments(apps, schema_editor):
|
||||||
FrontPort = apps.get_model('dcim', 'FrontPort')
|
FrontPort = apps.get_model('dcim', 'FrontPort')
|
||||||
PortAssignment = apps.get_model('dcim', 'PortAssignment')
|
PortAssignment = apps.get_model('dcim', 'PortAssignment')
|
||||||
@@ -38,6 +60,68 @@ class Migration(migrations.Migration):
|
|||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
# Create PortAssignmentTemplate model (for DeviceTypes)
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='PortAssignmentTemplate',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||||
|
(
|
||||||
|
'front_port_position',
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
validators=[
|
||||||
|
django.core.validators.MinValueValidator(1),
|
||||||
|
django.core.validators.MaxValueValidator(1024)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'rear_port_position',
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
validators=[
|
||||||
|
django.core.validators.MinValueValidator(1),
|
||||||
|
django.core.validators.MaxValueValidator(1024)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'front_port',
|
||||||
|
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dcim.frontporttemplate')
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'rear_port',
|
||||||
|
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dcim.rearporttemplate')
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='portassignmenttemplate',
|
||||||
|
constraint=models.UniqueConstraint(
|
||||||
|
fields=('front_port', 'front_port_position'),
|
||||||
|
name='dcim_portassignmenttemplate_unique_front_port_position'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='portassignmenttemplate',
|
||||||
|
constraint=models.UniqueConstraint(
|
||||||
|
fields=('rear_port', 'rear_port_position'),
|
||||||
|
name='dcim_portassignmenttemplate_unique_rear_port_position'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
# Add rear_ports ManyToManyField on FrontPortTemplate
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='frontporttemplate',
|
||||||
|
name='rear_ports',
|
||||||
|
field=models.ManyToManyField(
|
||||||
|
related_name='front_ports',
|
||||||
|
through='dcim.PortAssignmentTemplate',
|
||||||
|
to='dcim.rearporttemplate'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
# Create PortAssignment model (for Devices)
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='PortAssignment',
|
name='PortAssignment',
|
||||||
fields=[
|
fields=[
|
||||||
@@ -66,22 +150,39 @@ class Migration(migrations.Migration):
|
|||||||
('rear_port', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dcim.rearport')),
|
('rear_port', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dcim.rearport')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='portassignment',
|
||||||
|
constraint=models.UniqueConstraint(
|
||||||
|
fields=('front_port', 'front_port_position'),
|
||||||
|
name='dcim_portassignment_unique_front_port_position'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='portassignment',
|
||||||
|
constraint=models.UniqueConstraint(
|
||||||
|
fields=('rear_port', 'rear_port_position'),
|
||||||
|
name='dcim_portassignment_unique_rear_port_position'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
# Add rear_ports ManyToManyField on FrontPort
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='frontport',
|
model_name='frontport',
|
||||||
name='rear_ports',
|
name='rear_ports',
|
||||||
field=models.ManyToManyField(related_name='front_ports', through='dcim.PortAssignment', to='dcim.rearport'),
|
field=models.ManyToManyField(
|
||||||
),
|
related_name='front_ports',
|
||||||
migrations.AddConstraint(
|
through='dcim.PortAssignment',
|
||||||
model_name='portassignment',
|
to='dcim.rearport'
|
||||||
constraint=models.UniqueConstraint(
|
|
||||||
fields=('front_port', 'front_port_position'), name='dcim_portassignment_unique_front_port_position'
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
migrations.AddConstraint(
|
|
||||||
model_name='portassignment',
|
# Data migration
|
||||||
constraint=models.UniqueConstraint(
|
migrations.RunPython(
|
||||||
fields=('rear_port', 'rear_port_position'), name='dcim_portassignment_unique_rear_port_position'
|
code=populate_port_template_assignments,
|
||||||
),
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=populate_port_assignments,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
),
|
),
|
||||||
migrations.RunPython(code=populate_port_assignments, reverse_code=migrations.RunPython.noop),
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -9,6 +9,34 @@ class Migration(migrations.Migration):
|
|||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
# Remove rear_port & rear_port_position from FrontPortTemplate
|
||||||
|
migrations.RemoveConstraint(
|
||||||
|
model_name='frontporttemplate',
|
||||||
|
name='dcim_frontporttemplate_unique_rear_port_position',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='frontporttemplate',
|
||||||
|
name='rear_port',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='frontporttemplate',
|
||||||
|
name='rear_port_position',
|
||||||
|
),
|
||||||
|
|
||||||
|
# Add positions on FrontPortTemplate
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='frontporttemplate',
|
||||||
|
name='positions',
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
default=1,
|
||||||
|
validators=[
|
||||||
|
django.core.validators.MinValueValidator(1),
|
||||||
|
django.core.validators.MaxValueValidator(1024)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
# Remove rear_port & rear_port_position from FrontPort
|
||||||
migrations.RemoveConstraint(
|
migrations.RemoveConstraint(
|
||||||
model_name='frontport',
|
model_name='frontport',
|
||||||
name='dcim_frontport_unique_rear_port_position',
|
name='dcim_frontport_unique_rear_port_position',
|
||||||
@@ -21,6 +49,8 @@ class Migration(migrations.Migration):
|
|||||||
model_name='frontport',
|
model_name='frontport',
|
||||||
name='rear_port_position',
|
name='rear_port_position',
|
||||||
),
|
),
|
||||||
|
|
||||||
|
# Add positions on FrontPort
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='frontport',
|
model_name='frontport',
|
||||||
name='positions',
|
name='positions',
|
||||||
|
|||||||
@@ -518,6 +518,69 @@ class InterfaceTemplate(InterfaceValidationMixin, ModularComponentTemplateModel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PortAssignmentTemplate(models.Model):
|
||||||
|
"""
|
||||||
|
Maps a FrontPortTemplate & position to a RearPortTemplate & position.
|
||||||
|
"""
|
||||||
|
front_port = models.ForeignKey(
|
||||||
|
to='dcim.FrontPortTemplate',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
)
|
||||||
|
front_port_position = models.PositiveSmallIntegerField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
validators=(
|
||||||
|
MinValueValidator(PORT_POSITION_MIN),
|
||||||
|
MaxValueValidator(PORT_POSITION_MAX),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
rear_port = models.ForeignKey(
|
||||||
|
to='dcim.RearPortTemplate',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
)
|
||||||
|
rear_port_position = models.PositiveSmallIntegerField(
|
||||||
|
validators=(
|
||||||
|
MinValueValidator(PORT_POSITION_MIN),
|
||||||
|
MaxValueValidator(PORT_POSITION_MAX),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
constraints = (
|
||||||
|
models.UniqueConstraint(
|
||||||
|
fields=('front_port', 'front_port_position'),
|
||||||
|
name='%(app_label)s_%(class)s_unique_front_port_position'
|
||||||
|
),
|
||||||
|
models.UniqueConstraint(
|
||||||
|
fields=('rear_port', 'rear_port_position'),
|
||||||
|
name='%(app_label)s_%(class)s_unique_rear_port_position'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
|
||||||
|
# Validate rear port assignment
|
||||||
|
if self.front_port.device_type_id != self.rear_port.device_type_id:
|
||||||
|
raise ValidationError({
|
||||||
|
"rear_port": _("Rear port ({rear_port}) must belong to the same device type").format(
|
||||||
|
rear_port=self.rear_port
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
# Validate rear port position assignment
|
||||||
|
if self.rear_port_position > self.rear_port.positions:
|
||||||
|
raise ValidationError({
|
||||||
|
"rear_port_position": _(
|
||||||
|
"Invalid rear port position ({rear_port_position}): Rear port {name} has only {positions} "
|
||||||
|
"positions."
|
||||||
|
).format(
|
||||||
|
rear_port_position=self.rear_port_position,
|
||||||
|
name=self.rear_port.name,
|
||||||
|
positions=self.rear_port.positions
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class FrontPortTemplate(ModularComponentTemplateModel):
|
class FrontPortTemplate(ModularComponentTemplateModel):
|
||||||
"""
|
"""
|
||||||
Template for a pass-through port on the front of a new Device.
|
Template for a pass-through port on the front of a new Device.
|
||||||
@@ -531,18 +594,18 @@ class FrontPortTemplate(ModularComponentTemplateModel):
|
|||||||
verbose_name=_('color'),
|
verbose_name=_('color'),
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
rear_port = models.ForeignKey(
|
positions = models.PositiveSmallIntegerField(
|
||||||
to='dcim.RearPortTemplate',
|
verbose_name=_('positions'),
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='frontport_templates'
|
|
||||||
)
|
|
||||||
rear_port_position = models.PositiveSmallIntegerField(
|
|
||||||
verbose_name=_('rear port position'),
|
|
||||||
default=1,
|
default=1,
|
||||||
validators=[
|
validators=[
|
||||||
MinValueValidator(PORT_POSITION_MIN),
|
MinValueValidator(PORT_POSITION_MIN),
|
||||||
MaxValueValidator(PORT_POSITION_MAX)
|
MaxValueValidator(PORT_POSITION_MAX)
|
||||||
]
|
],
|
||||||
|
)
|
||||||
|
rear_ports = models.ManyToManyField(
|
||||||
|
to='dcim.RearPortTemplate',
|
||||||
|
through='dcim.PortAssignmentTemplate',
|
||||||
|
related_name='front_ports',
|
||||||
)
|
)
|
||||||
|
|
||||||
component_model = FrontPort
|
component_model = FrontPort
|
||||||
@@ -557,51 +620,17 @@ class FrontPortTemplate(ModularComponentTemplateModel):
|
|||||||
fields=('module_type', 'name'),
|
fields=('module_type', 'name'),
|
||||||
name='%(app_label)s_%(class)s_unique_module_type_name'
|
name='%(app_label)s_%(class)s_unique_module_type_name'
|
||||||
),
|
),
|
||||||
models.UniqueConstraint(
|
|
||||||
fields=('rear_port', 'rear_port_position'),
|
|
||||||
name='%(app_label)s_%(class)s_unique_rear_port_position'
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
verbose_name = _('front port template')
|
verbose_name = _('front port template')
|
||||||
verbose_name_plural = _('front port templates')
|
verbose_name_plural = _('front port templates')
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
super().clean()
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
# Validate rear port assignment
|
|
||||||
if self.rear_port.device_type != self.device_type:
|
|
||||||
raise ValidationError(
|
|
||||||
_("Rear port ({name}) must belong to the same device type").format(name=self.rear_port)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Validate rear port position assignment
|
|
||||||
if self.rear_port_position > self.rear_port.positions:
|
|
||||||
raise ValidationError(
|
|
||||||
_("Invalid rear port position ({position}); rear port {name} has only {count} positions").format(
|
|
||||||
position=self.rear_port_position,
|
|
||||||
name=self.rear_port.name,
|
|
||||||
count=self.rear_port.positions
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
except RearPortTemplate.DoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def instantiate(self, **kwargs):
|
def instantiate(self, **kwargs):
|
||||||
if self.rear_port:
|
|
||||||
rear_port_name = self.rear_port.resolve_name(kwargs.get('module'))
|
|
||||||
rear_port = RearPort.objects.get(name=rear_port_name, **kwargs)
|
|
||||||
else:
|
|
||||||
rear_port = None
|
|
||||||
return self.component_model(
|
return self.component_model(
|
||||||
name=self.resolve_name(kwargs.get('module')),
|
name=self.resolve_name(kwargs.get('module')),
|
||||||
label=self.resolve_label(kwargs.get('module')),
|
label=self.resolve_label(kwargs.get('module')),
|
||||||
type=self.type,
|
type=self.type,
|
||||||
color=self.color,
|
color=self.color,
|
||||||
rear_port=rear_port,
|
positions=self.positions,
|
||||||
rear_port_position=self.rear_port_position,
|
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
instantiate.do_not_call_in_templates = True
|
instantiate.do_not_call_in_templates = True
|
||||||
@@ -611,8 +640,7 @@ class FrontPortTemplate(ModularComponentTemplateModel):
|
|||||||
'name': self.name,
|
'name': self.name,
|
||||||
'type': self.type,
|
'type': self.type,
|
||||||
'color': self.color,
|
'color': self.color,
|
||||||
'rear_port': self.rear_port.name,
|
'positions': self.positions,
|
||||||
'rear_port_position': self.rear_port_position,
|
|
||||||
'label': self.label,
|
'label': self.label,
|
||||||
'description': self.description,
|
'description': self.description,
|
||||||
}
|
}
|
||||||
@@ -637,7 +665,7 @@ class RearPortTemplate(ModularComponentTemplateModel):
|
|||||||
validators=[
|
validators=[
|
||||||
MinValueValidator(PORT_POSITION_MIN),
|
MinValueValidator(PORT_POSITION_MIN),
|
||||||
MaxValueValidator(PORT_POSITION_MAX)
|
MaxValueValidator(PORT_POSITION_MAX)
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
component_model = RearPort
|
component_model = RearPort
|
||||||
|
|||||||
Reference in New Issue
Block a user