mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-20 04:12:25 -06:00
Remove rear_ports M2M fields from FrontPort & FrontPortTemplate
This commit is contained in:
@@ -5,7 +5,8 @@ from dcim.choices import *
|
|||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import (
|
from dcim.models import (
|
||||||
ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, FrontPortTemplate, InterfaceTemplate,
|
ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, FrontPortTemplate, InterfaceTemplate,
|
||||||
InventoryItemTemplate, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate, RearPortTemplate,
|
InventoryItemTemplate, ModuleBayTemplate, PortAssignmentTemplate, PowerOutletTemplate, PowerPortTemplate,
|
||||||
|
RearPortTemplate,
|
||||||
)
|
)
|
||||||
from netbox.api.fields import ChoiceField, ContentTypeField
|
from netbox.api.fields import ChoiceField, ContentTypeField
|
||||||
from netbox.api.gfk_fields import GFKSerializerField
|
from netbox.api.gfk_fields import GFKSerializerField
|
||||||
@@ -205,6 +206,16 @@ class InterfaceTemplateSerializer(ComponentTemplateSerializer):
|
|||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
|
class RearPortTemplateAssignmentSerializer(serializers.ModelSerializer):
|
||||||
|
front_port = serializers.PrimaryKeyRelatedField(
|
||||||
|
queryset=FrontPortTemplate.objects.all(),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = PortAssignmentTemplate
|
||||||
|
fields = ('id', 'rear_port_position', 'front_port', 'front_port_position')
|
||||||
|
|
||||||
|
|
||||||
class RearPortTemplateSerializer(ComponentTemplateSerializer):
|
class RearPortTemplateSerializer(ComponentTemplateSerializer):
|
||||||
device_type = DeviceTypeSerializer(
|
device_type = DeviceTypeSerializer(
|
||||||
required=False,
|
required=False,
|
||||||
@@ -219,15 +230,52 @@ class RearPortTemplateSerializer(ComponentTemplateSerializer):
|
|||||||
default=None
|
default=None
|
||||||
)
|
)
|
||||||
type = ChoiceField(choices=PortTypeChoices)
|
type = ChoiceField(choices=PortTypeChoices)
|
||||||
|
front_ports = RearPortTemplateAssignmentSerializer(
|
||||||
|
source='assignments',
|
||||||
|
many=True,
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RearPortTemplate
|
model = RearPortTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'color',
|
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'color', 'positions',
|
||||||
'positions', 'description', 'created', 'last_updated',
|
'front_ports', 'description', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
assignments = validated_data.pop('assignments', [])
|
||||||
|
instance = super().create(validated_data)
|
||||||
|
|
||||||
|
# Create FrontPort assignments
|
||||||
|
for assignment_data in assignments:
|
||||||
|
PortAssignmentTemplate.objects.create(rear_port=instance, **assignment_data)
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
assignments = validated_data.pop('assignments', None)
|
||||||
|
instance = super().update(instance, validated_data)
|
||||||
|
|
||||||
|
if assignments is not None:
|
||||||
|
# Update FrontPort assignments
|
||||||
|
PortAssignmentTemplate.objects.filter(rear_port=instance).delete()
|
||||||
|
for assignment_data in assignments:
|
||||||
|
PortAssignmentTemplate.objects.create(rear_port=instance, **assignment_data)
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class FrontPortTemplateAssignmentSerializer(serializers.ModelSerializer):
|
||||||
|
rear_port = serializers.PrimaryKeyRelatedField(
|
||||||
|
queryset=RearPortTemplate.objects.all(),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = PortAssignmentTemplate
|
||||||
|
fields = ('id', 'front_port_position', 'rear_port', 'rear_port_position')
|
||||||
|
|
||||||
|
|
||||||
class FrontPortTemplateSerializer(ComponentTemplateSerializer):
|
class FrontPortTemplateSerializer(ComponentTemplateSerializer):
|
||||||
device_type = DeviceTypeSerializer(
|
device_type = DeviceTypeSerializer(
|
||||||
@@ -243,7 +291,11 @@ class FrontPortTemplateSerializer(ComponentTemplateSerializer):
|
|||||||
default=None
|
default=None
|
||||||
)
|
)
|
||||||
type = ChoiceField(choices=PortTypeChoices)
|
type = ChoiceField(choices=PortTypeChoices)
|
||||||
rear_ports = RearPortTemplateSerializer(nested=True, many=True)
|
rear_ports = FrontPortTemplateAssignmentSerializer(
|
||||||
|
source='assignments',
|
||||||
|
many=True,
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPortTemplate
|
model = FrontPortTemplate
|
||||||
@@ -253,6 +305,28 @@ class FrontPortTemplateSerializer(ComponentTemplateSerializer):
|
|||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
assignments = validated_data.pop('assignments', [])
|
||||||
|
instance = super().create(validated_data)
|
||||||
|
|
||||||
|
# Create RearPort assignments
|
||||||
|
for assignment_data in assignments:
|
||||||
|
PortAssignmentTemplate.objects.create(front_port=instance, **assignment_data)
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
assignments = validated_data.pop('assignments', None)
|
||||||
|
instance = super().update(instance, validated_data)
|
||||||
|
|
||||||
|
if assignments is not None:
|
||||||
|
# Update RearPort assignments
|
||||||
|
PortAssignmentTemplate.objects.filter(front_port=instance).delete()
|
||||||
|
for assignment_data in assignments:
|
||||||
|
PortAssignmentTemplate.objects.create(front_port=instance, **assignment_data)
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
class ModuleBayTemplateSerializer(ComponentTemplateSerializer):
|
class ModuleBayTemplateSerializer(ComponentTemplateSerializer):
|
||||||
device_type = DeviceTypeSerializer(
|
device_type = DeviceTypeSerializer(
|
||||||
|
|||||||
@@ -884,10 +884,11 @@ class FrontPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeCo
|
|||||||
choices=PortTypeChoices,
|
choices=PortTypeChoices,
|
||||||
null_value=None
|
null_value=None
|
||||||
)
|
)
|
||||||
rear_port_template_id = django_filters.ModelMultipleChoiceFilter(
|
rear_port_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='rear_ports',
|
field_name='assignments__rear_port',
|
||||||
queryset=FrontPortTemplate.objects.all(),
|
queryset=RearPort.objects.all(),
|
||||||
label=_('Rear port template (ID)'),
|
to_field_name='rear_port',
|
||||||
|
label=_('Rear port (ID)'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -900,10 +901,11 @@ class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeCom
|
|||||||
choices=PortTypeChoices,
|
choices=PortTypeChoices,
|
||||||
null_value=None
|
null_value=None
|
||||||
)
|
)
|
||||||
front_port_template_id = django_filters.ModelMultipleChoiceFilter(
|
front_port_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='front_ports',
|
field_name='assignments__front_port',
|
||||||
queryset=FrontPortTemplate.objects.all(),
|
queryset=FrontPort.objects.all(),
|
||||||
label=_('Front port template (ID)'),
|
to_field_name='front_port',
|
||||||
|
label=_('Front port (ID)'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -2109,8 +2111,9 @@ class FrontPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet)
|
|||||||
null_value=None
|
null_value=None
|
||||||
)
|
)
|
||||||
rear_port_id = django_filters.ModelMultipleChoiceFilter(
|
rear_port_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='rear_ports',
|
field_name='assignments__rear_port',
|
||||||
queryset=RearPort.objects.all(),
|
queryset=RearPort.objects.all(),
|
||||||
|
to_field_name='rear_port',
|
||||||
label=_('Rear port (ID)'),
|
label=_('Rear port (ID)'),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -2128,8 +2131,9 @@ class RearPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet):
|
|||||||
null_value=None
|
null_value=None
|
||||||
)
|
)
|
||||||
front_port_id = django_filters.ModelMultipleChoiceFilter(
|
front_port_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='front_ports',
|
field_name='assignments__front_port',
|
||||||
queryset=FrontPort.objects.all(),
|
queryset=FrontPort.objects.all(),
|
||||||
|
to_field_name='front_port',
|
||||||
label=_('Front port (ID)'),
|
label=_('Front port (ID)'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -133,8 +133,6 @@ class FrontPortTemplateCreateForm(ComponentCreateForm, model_forms.FrontPortTemp
|
|||||||
|
|
||||||
# Check that the number of FrontPortTemplates to be created matches the selected number of RearPortTemplate
|
# Check that the number of FrontPortTemplates to be created matches the selected number of RearPortTemplate
|
||||||
# positions
|
# positions
|
||||||
print(f"name: {self.cleaned_data['name']}")
|
|
||||||
print(f"rear_ports: {self.cleaned_data['rear_ports']}")
|
|
||||||
frontport_count = len(self.cleaned_data['name'])
|
frontport_count = len(self.cleaned_data['name'])
|
||||||
rearport_count = len(self.cleaned_data['rear_ports'])
|
rearport_count = len(self.cleaned_data['rear_ports'])
|
||||||
if frontport_count != rearport_count:
|
if frontport_count != rearport_count:
|
||||||
|
|||||||
@@ -385,7 +385,8 @@ class DeviceTypeType(PrimaryObjectType):
|
|||||||
)
|
)
|
||||||
class FrontPortType(ModularComponentType, CabledObjectMixin):
|
class FrontPortType(ModularComponentType, CabledObjectMixin):
|
||||||
color: str
|
color: str
|
||||||
# rear_port: Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')]
|
|
||||||
|
assignments: List[Annotated["PortAssignmentType", strawberry.lazy('dcim.graphql.types')]]
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.type(
|
@strawberry_django.type(
|
||||||
@@ -396,7 +397,8 @@ class FrontPortType(ModularComponentType, CabledObjectMixin):
|
|||||||
)
|
)
|
||||||
class FrontPortTemplateType(ModularComponentTemplateType):
|
class FrontPortTemplateType(ModularComponentTemplateType):
|
||||||
color: str
|
color: str
|
||||||
# rear_port: Annotated["RearPortTemplateType", strawberry.lazy('dcim.graphql.types')]
|
|
||||||
|
assignments: List[Annotated["PortAssignmentTemplateType", strawberry.lazy('dcim.graphql.types')]]
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.type(
|
@strawberry_django.type(
|
||||||
@@ -636,6 +638,28 @@ class PlatformType(NestedGroupObjectType):
|
|||||||
devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
|
devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry_django.type(
|
||||||
|
models.PortAssignment,
|
||||||
|
fields='__all__',
|
||||||
|
# filters=PortAssignmentFilter,
|
||||||
|
pagination=True
|
||||||
|
)
|
||||||
|
class PortAssignmentType(ModularComponentTemplateType):
|
||||||
|
front_port: Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')]
|
||||||
|
rear_port: Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')]
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry_django.type(
|
||||||
|
models.PortAssignmentTemplate,
|
||||||
|
fields='__all__',
|
||||||
|
# filters=PortAssignmentTemplateFilter,
|
||||||
|
pagination=True
|
||||||
|
)
|
||||||
|
class PortAssignmentTemplateType(ModularComponentTemplateType):
|
||||||
|
front_port_template: Annotated["FrontPortTemplateType", strawberry.lazy('dcim.graphql.types')]
|
||||||
|
rear_port_template: Annotated["RearPortTemplateType", strawberry.lazy('dcim.graphql.types')]
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.type(
|
@strawberry_django.type(
|
||||||
models.PowerFeed,
|
models.PowerFeed,
|
||||||
exclude=['_path'],
|
exclude=['_path'],
|
||||||
@@ -768,7 +792,7 @@ class RackRoleType(OrganizationalObjectType):
|
|||||||
class RearPortType(ModularComponentType, CabledObjectMixin):
|
class RearPortType(ModularComponentType, CabledObjectMixin):
|
||||||
color: str
|
color: str
|
||||||
|
|
||||||
front_ports: List[Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')]]
|
assignments: List[Annotated["PortAssignmentType", strawberry.lazy('dcim.graphql.types')]]
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.type(
|
@strawberry_django.type(
|
||||||
@@ -780,7 +804,7 @@ class RearPortType(ModularComponentType, CabledObjectMixin):
|
|||||||
class RearPortTemplateType(ModularComponentTemplateType):
|
class RearPortTemplateType(ModularComponentTemplateType):
|
||||||
color: str
|
color: str
|
||||||
|
|
||||||
front_ports: List[Annotated["FrontPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
|
assignments: List[Annotated["PortAssignmentTemplateType", strawberry.lazy('dcim.graphql.types')]]
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.type(
|
@strawberry_django.type(
|
||||||
|
|||||||
@@ -118,17 +118,6 @@ class Migration(migrations.Migration):
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
# 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)
|
# Create PortAssignment model (for Devices)
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='PortAssignment',
|
name='PortAssignment',
|
||||||
@@ -187,17 +176,6 @@ class Migration(migrations.Migration):
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
# Add rear_ports ManyToManyField on FrontPort
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='frontport',
|
|
||||||
name='rear_ports',
|
|
||||||
field=models.ManyToManyField(
|
|
||||||
related_name='front_ports',
|
|
||||||
through='dcim.PortAssignment',
|
|
||||||
to='dcim.rearport'
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
# Data migration
|
# Data migration
|
||||||
migrations.RunPython(
|
migrations.RunPython(
|
||||||
code=populate_port_template_assignments,
|
code=populate_port_template_assignments,
|
||||||
|
|||||||
@@ -568,11 +568,6 @@ class FrontPortTemplate(ModularComponentTemplateModel):
|
|||||||
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
|
||||||
|
|
||||||
|
|||||||
@@ -1119,13 +1119,8 @@ class FrontPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin):
|
|||||||
MaxValueValidator(PORT_POSITION_MAX)
|
MaxValueValidator(PORT_POSITION_MAX)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
rear_ports = models.ManyToManyField(
|
|
||||||
to='dcim.RearPort',
|
|
||||||
through='dcim.PortAssignment',
|
|
||||||
related_name='front_ports',
|
|
||||||
)
|
|
||||||
|
|
||||||
clone_fields = ('device', 'type', 'color')
|
clone_fields = ('device', 'type', 'color', 'positions')
|
||||||
|
|
||||||
class Meta(ModularComponentModel.Meta):
|
class Meta(ModularComponentModel.Meta):
|
||||||
constraints = (
|
constraints = (
|
||||||
@@ -1159,6 +1154,7 @@ class RearPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin):
|
|||||||
MaxValueValidator(PORT_POSITION_MAX)
|
MaxValueValidator(PORT_POSITION_MAX)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
clone_fields = ('device', 'type', 'color', 'positions')
|
clone_fields = ('device', 'type', 'color', 'positions')
|
||||||
|
|
||||||
class Meta(ModularComponentModel.Meta):
|
class Meta(ModularComponentModel.Meta):
|
||||||
@@ -1170,13 +1166,13 @@ class RearPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin):
|
|||||||
|
|
||||||
# Check that positions count is greater than or equal to the number of associated FrontPorts
|
# Check that positions count is greater than or equal to the number of associated FrontPorts
|
||||||
if not self._state.adding:
|
if not self._state.adding:
|
||||||
frontport_count = self.front_ports.count()
|
assignment_count = self.assignments.count()
|
||||||
if self.positions < frontport_count:
|
if self.positions < assignment_count:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
"positions": _(
|
"positions": _(
|
||||||
"The number of positions cannot be less than the number of mapped front ports "
|
"The number of positions cannot be less than the number of mapped front ports "
|
||||||
"({frontport_count})"
|
"({count})"
|
||||||
).format(frontport_count=frontport_count)
|
).format(count=assignment_count)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -156,8 +156,8 @@ def extend_rearport_cable_paths(instance, created, raw, **kwargs):
|
|||||||
When a new FrontPort is created, add it to any CablePaths which end at its corresponding RearPort.
|
When a new FrontPort is created, add it to any CablePaths which end at its corresponding RearPort.
|
||||||
"""
|
"""
|
||||||
if created and not raw:
|
if created and not raw:
|
||||||
for rear_port in instance.rear_ports.all():
|
for assignment in instance.assignments.prefetch_related('rear_port'):
|
||||||
for cablepath in CablePath.objects.filter(_nodes__contains=rear_port):
|
for cablepath in CablePath.objects.filter(_nodes__contains=assignment.rear_port):
|
||||||
cablepath.retrace()
|
cablepath.retrace()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -999,29 +999,49 @@ class FrontPortTemplateTest(APIViewTestCases.APIViewTestCase):
|
|||||||
'device_type': devicetype.pk,
|
'device_type': devicetype.pk,
|
||||||
'name': 'Front Port Template 3',
|
'name': 'Front Port Template 3',
|
||||||
'type': PortTypeChoices.TYPE_8P8C,
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
'rear_port': rear_port_templates[2].pk,
|
'rear_ports': [
|
||||||
'rear_port_position': 1,
|
{
|
||||||
|
'front_port_position': 1,
|
||||||
|
'rear_port': rear_port_templates[2].pk,
|
||||||
|
'rear_port_position': 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'device_type': devicetype.pk,
|
'device_type': devicetype.pk,
|
||||||
'name': 'Front Port Template 4',
|
'name': 'Front Port Template 4',
|
||||||
'type': PortTypeChoices.TYPE_8P8C,
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
'rear_port': rear_port_templates[3].pk,
|
'rear_ports': [
|
||||||
'rear_port_position': 1,
|
{
|
||||||
|
'front_port_position': 1,
|
||||||
|
'rear_port': rear_port_templates[3].pk,
|
||||||
|
'rear_port_position': 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'module_type': moduletype.pk,
|
'module_type': moduletype.pk,
|
||||||
'name': 'Front Port Template 7',
|
'name': 'Front Port Template 7',
|
||||||
'type': PortTypeChoices.TYPE_8P8C,
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
'rear_port': rear_port_templates[6].pk,
|
'rear_ports': [
|
||||||
'rear_port_position': 1,
|
{
|
||||||
|
'front_port_position': 1,
|
||||||
|
'rear_port': rear_port_templates[6].pk,
|
||||||
|
'rear_port_position': 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'module_type': moduletype.pk,
|
'module_type': moduletype.pk,
|
||||||
'name': 'Front Port Template 8',
|
'name': 'Front Port Template 8',
|
||||||
'type': PortTypeChoices.TYPE_8P8C,
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
'rear_port': rear_port_templates[7].pk,
|
'rear_ports': [
|
||||||
'rear_port_position': 1,
|
{
|
||||||
|
'front_port_position': 1,
|
||||||
|
'rear_port': rear_port_templates[7].pk,
|
||||||
|
'rear_port_position': 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user