Add manufacturer field to RackType

This commit is contained in:
Jeremy Stretch 2024-07-14 16:33:30 -04:00
parent a54655cc94
commit 2bde82e37b
16 changed files with 169 additions and 81 deletions

View File

@ -4,6 +4,10 @@ A rack type defines the physical characteristics of a particular model of [rack]
## Fields ## Fields
### Manufacturer
The [manufacturer](./manufacturer.md) which produces this type of rack.
### Name ### Name
The unique name of the rack type. The unique name of the rack type.

View File

@ -9,6 +9,7 @@ from netbox.api.serializers import NetBoxModelSerializer
from netbox.config import ConfigItem from netbox.config import ConfigItem
from tenancy.api.serializers_.tenants import TenantSerializer from tenancy.api.serializers_.tenants import TenantSerializer
from users.api.serializers_.users import UserSerializer from users.api.serializers_.users import UserSerializer
from .manufacturers import ManufacturerSerializer
from .sites import LocationSerializer, SiteSerializer from .sites import LocationSerializer, SiteSerializer
__all__ = ( __all__ = (
@ -35,6 +36,9 @@ class RackRoleSerializer(NetBoxModelSerializer):
class RackTypeSerializer(NetBoxModelSerializer): class RackTypeSerializer(NetBoxModelSerializer):
manufacturer = ManufacturerSerializer(
nested=True
)
type = ChoiceField( type = ChoiceField(
choices=RackTypeChoices, choices=RackTypeChoices,
allow_blank=True, allow_blank=True,
@ -61,12 +65,12 @@ class RackTypeSerializer(NetBoxModelSerializer):
class Meta: class Meta:
model = RackType model = RackType
fields = [ fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'type', 'width', 'u_height', 'id', 'url', 'display_url', 'display', 'manufacturer', 'name', 'slug', 'description', 'type', 'width',
'starting_unit', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'weight', 'max_weight', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'weight',
'weight_unit', 'mounting_depth', 'description', 'comments', 'tags', 'custom_fields', 'created', 'max_weight', 'weight_unit', 'mounting_depth', 'description', 'comments', 'tags', 'custom_fields',
'last_updated', 'created', 'last_updated',
] ]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description') brief_fields = ('id', 'url', 'display', 'manufacturer', 'name', 'slug', 'description')
class RackSerializer(NetBoxModelSerializer): class RackSerializer(NetBoxModelSerializer):

View File

@ -291,6 +291,16 @@ class RackRoleFilterSet(OrganizationalModelFilterSet):
class RackTypeFilterSet(NetBoxModelFilterSet): class RackTypeFilterSet(NetBoxModelFilterSet):
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
queryset=Manufacturer.objects.all(),
label=_('Manufacturer (ID)'),
)
manufacturer = django_filters.ModelMultipleChoiceFilter(
field_name='manufacturer__slug',
queryset=Manufacturer.objects.all(),
to_field_name='slug',
label=_('Manufacturer (slug)'),
)
type = django_filters.MultipleChoiceFilter( type = django_filters.MultipleChoiceFilter(
choices=RackTypeChoices choices=RackTypeChoices
) )
@ -301,8 +311,8 @@ class RackTypeFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = RackType model = RackType
fields = ( fields = (
'id', 'name', 'slug', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'id', 'name', 'slug', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit',
'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description',
) )
def search(self, queryset, name, value): def search(self, queryset, name, value):

View File

@ -220,6 +220,11 @@ class RackRoleBulkEditForm(NetBoxModelBulkEditForm):
class RackTypeBulkEditForm(NetBoxModelBulkEditForm): class RackTypeBulkEditForm(NetBoxModelBulkEditForm):
manufacturer = DynamicModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
required=False
)
type = forms.ChoiceField( type = forms.ChoiceField(
label=_('Type'), label=_('Type'),
choices=add_blank_choice(RackTypeChoices), choices=add_blank_choice(RackTypeChoices),
@ -288,12 +293,14 @@ class RackTypeBulkEditForm(NetBoxModelBulkEditForm):
model = RackType model = RackType
fieldsets = ( fieldsets = (
FieldSet('description', 'type', name=_('Rack Type')), FieldSet('manufacturer', 'description', 'type', name=_('Rack Type')),
FieldSet( FieldSet(
'width', 'u_height', 'width',
'u_height',
InlineFields('outer_width', 'outer_depth', 'outer_unit', label=_('Outer Dimensions')), InlineFields('outer_width', 'outer_depth', 'outer_unit', label=_('Outer Dimensions')),
InlineFields('weight', 'max_weight', 'weight_unit', label=_('Weight')), InlineFields('weight', 'max_weight', 'weight_unit', label=_('Weight')),
'mounting_depth', name=_('Dimensions') 'mounting_depth',
name=_('Dimensions')
), ),
FieldSet('starting_unit', 'desc_units', name=_('Numbering')), FieldSet('starting_unit', 'desc_units', name=_('Numbering')),
) )

View File

@ -178,6 +178,12 @@ class RackRoleImportForm(NetBoxModelImportForm):
class RackTypeImportForm(NetBoxModelImportForm): class RackTypeImportForm(NetBoxModelImportForm):
manufacturer = forms.ModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
to_field_name='name',
help_text=_('The manufacturer of this rack type')
)
type = CSVChoiceField( type = CSVChoiceField(
label=_('Type'), label=_('Type'),
choices=RackTypeChoices, choices=RackTypeChoices,
@ -210,9 +216,9 @@ class RackTypeImportForm(NetBoxModelImportForm):
class Meta: class Meta:
model = RackType model = RackType
fields = ( fields = (
'name', 'slug', 'type', 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'manufacturer', 'name', 'slug', 'type', 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width',
'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description',
'max_weight', 'weight_unit', 'description', 'comments', 'tags', 'comments', 'tags',
) )
def __init__(self, data=None, *args, **kwargs): def __init__(self, data=None, *args, **kwargs):

View File

@ -248,7 +248,12 @@ class RackTypeFilterForm(NetBoxModelFilterSetForm):
FieldSet('type', 'width', 'u_height', 'starting_unit', name=_('Rack Type')), FieldSet('type', 'width', 'u_height', 'starting_unit', name=_('Rack Type')),
FieldSet('weight', 'max_weight', 'weight_unit', name=_('Weight')), FieldSet('weight', 'max_weight', 'weight_unit', name=_('Weight')),
) )
selector_fields = ('filter_id', 'q',) selector_fields = ('filter_id', 'q', 'manufacturer_id')
manufacturer_id = DynamicModelMultipleChoiceField(
queryset=Manufacturer.objects.all(),
required=False,
label=_('Manufacturer')
)
type = forms.MultipleChoiceField( type = forms.MultipleChoiceField(
label=_('Type'), label=_('Type'),
choices=RackTypeChoices, choices=RackTypeChoices,

View File

@ -203,11 +203,15 @@ class RackRoleForm(NetBoxModelForm):
class RackTypeForm(NetBoxModelForm): class RackTypeForm(NetBoxModelForm):
manufacturer = DynamicModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all()
)
comments = CommentField() comments = CommentField()
slug = SlugField() slug = SlugField()
fieldsets = ( fieldsets = (
FieldSet('name', 'slug', 'description', 'type', 'tags', name=_('Rack')), FieldSet('manufacturer', 'name', 'slug', 'description', 'type', 'tags', name=_('Rack Type')),
FieldSet( FieldSet(
'width', 'u_height', 'width', 'u_height',
InlineFields('outer_width', 'outer_depth', 'outer_unit', label=_('Outer Dimensions')), InlineFields('outer_width', 'outer_depth', 'outer_unit', label=_('Outer Dimensions')),
@ -220,9 +224,9 @@ class RackTypeForm(NetBoxModelForm):
class Meta: class Meta:
model = RackType model = RackType
fields = [ fields = [
'name', 'slug', 'type', 'width', 'u_height', 'starting_unit', 'desc_units', 'manufacturer', 'name', 'slug', 'type', 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width',
'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description',
'weight_unit', 'description', 'comments', 'tags', 'comments', 'tags',
] ]

View File

@ -614,6 +614,7 @@ class PowerPortTemplateType(ModularComponentTemplateType):
) )
class RackTypeType(NetBoxObjectType): class RackTypeType(NetBoxObjectType):
_name: str _name: str
manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')]
@strawberry_django.type( @strawberry_django.type(

View File

@ -1,9 +1,8 @@
# Generated by Django 4.2.11 on 2024-06-25 16:43
import django.core.validators import django.core.validators
from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import taggit.managers import taggit.managers
from django.db import migrations, models
import utilities.fields import utilities.fields
import utilities.json import utilities.json
import utilities.ordering import utilities.ordering
@ -23,41 +22,43 @@ class Migration(migrations.Migration):
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True, null=True)), ('created', models.DateTimeField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)), ('last_updated', models.DateTimeField(auto_now=True, null=True)),
( ('custom_field_data', models.JSONField(
'custom_field_data', blank=True,
models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder), default=dict,
), encoder=utilities.json.CustomFieldJSONEncoder
)),
('description', models.CharField(blank=True, max_length=200)), ('description', models.CharField(blank=True, max_length=200)),
('comments', models.TextField(blank=True)), ('comments', models.TextField(blank=True)),
('weight', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True)), ('weight', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True)),
('weight_unit', models.CharField(blank=True, max_length=50)), ('weight_unit', models.CharField(blank=True, max_length=50)),
('_abs_weight', models.PositiveBigIntegerField(blank=True, null=True)), ('_abs_weight', models.PositiveBigIntegerField(blank=True, null=True)),
('manufacturer', models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name='rack_types',
to='dcim.manufacturer'
)),
('name', models.CharField(max_length=100)), ('name', models.CharField(max_length=100)),
( ('_name', utilities.fields.NaturalOrderingField(
'_name', 'name',
utilities.fields.NaturalOrderingField( blank=True,
'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize max_length=100,
), naturalize_function=utilities.ordering.naturalize
),
), ),
('slug', models.SlugField(max_length=100, unique=True)), ('slug', models.SlugField(max_length=100, unique=True)),
('type', models.CharField(blank=True, max_length=50)), ('type', models.CharField(blank=True, max_length=50)),
('width', models.PositiveSmallIntegerField(default=19)), ('width', models.PositiveSmallIntegerField(default=19)),
( ('u_height', models.PositiveSmallIntegerField(
'u_height', default=42,
models.PositiveSmallIntegerField( validators=[
default=42, django.core.validators.MinValueValidator(1),
validators=[ django.core.validators.MaxValueValidator(100),
django.core.validators.MinValueValidator(1), ]
django.core.validators.MaxValueValidator(100), )),
], ('starting_unit', models.PositiveSmallIntegerField(
), default=1,
), validators=[django.core.validators.MinValueValidator(1)]
( )),
'starting_unit',
models.PositiveSmallIntegerField(
default=1, validators=[django.core.validators.MinValueValidator(1)]
),
),
('desc_units', models.BooleanField(default=False)), ('desc_units', models.BooleanField(default=False)),
('outer_width', models.PositiveSmallIntegerField(blank=True, null=True)), ('outer_width', models.PositiveSmallIntegerField(blank=True, null=True)),
('outer_depth', models.PositiveSmallIntegerField(blank=True, null=True)), ('outer_depth', models.PositiveSmallIntegerField(blank=True, null=True)),

View File

@ -48,6 +48,11 @@ class RackType(WeightMixin, PrimaryModel):
'mounting_depth' 'mounting_depth'
] ]
manufacturer = models.ForeignKey(
to='dcim.Manufacturer',
on_delete=models.PROTECT,
related_name='rack_types'
)
name = models.CharField( name = models.CharField(
verbose_name=_('name'), verbose_name=_('name'),
max_length=100 max_length=100
@ -83,7 +88,7 @@ class RackType(WeightMixin, PrimaryModel):
starting_unit = models.PositiveSmallIntegerField( starting_unit = models.PositiveSmallIntegerField(
default=RACK_STARTING_UNIT_DEFAULT, default=RACK_STARTING_UNIT_DEFAULT,
verbose_name=_('starting unit'), verbose_name=_('starting unit'),
validators=[MinValueValidator(1),], validators=[MinValueValidator(1)],
help_text=_('Starting unit for rack') help_text=_('Starting unit for rack')
) )
desc_units = models.BooleanField( desc_units = models.BooleanField(
@ -107,7 +112,7 @@ class RackType(WeightMixin, PrimaryModel):
verbose_name=_('outer unit'), verbose_name=_('outer unit'),
max_length=50, max_length=50,
choices=RackDimensionUnitChoices, choices=RackDimensionUnitChoices,
blank=True, blank=True
) )
max_weight = models.PositiveIntegerField( max_weight = models.PositiveIntegerField(
verbose_name=_('max weight'), verbose_name=_('max weight'),
@ -131,11 +136,11 @@ class RackType(WeightMixin, PrimaryModel):
) )
clone_fields = ( clone_fields = (
'type', 'width', 'u_height', 'desc_units', 'outer_width', 'manufacturer', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit',
'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit',
) )
prerequisite_models = ( prerequisite_models = (
'dcim.Site', 'dcim.Manufacturer',
) )
class Meta: class Meta:
@ -205,7 +210,6 @@ class RackType(WeightMixin, PrimaryModel):
# Racks # Racks
# #
class RackRole(OrganizationalModel): class RackRole(OrganizationalModel):
""" """
Racks can be organized by functional role, similar to Devices. Racks can be organized by functional role, similar to Devices.

View File

@ -55,6 +55,10 @@ class RackTypeTable(NetBoxTable):
order_by=('_name',), order_by=('_name',),
linkify=True linkify=True
) )
manufacturer = tables.Column(
verbose_name=_('Manufacturer'),
linkify=True
)
u_height = tables.TemplateColumn( u_height = tables.TemplateColumn(
template_code="{{ value }}U", template_code="{{ value }}U",
verbose_name=_('Height') verbose_name=_('Height')
@ -87,11 +91,12 @@ class RackTypeTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = RackType model = RackType
fields = ( fields = (
'pk', 'id', 'name', 'type', 'u_height', 'starting_unit', 'width', 'outer_width', 'outer_depth', 'pk', 'id', 'name', 'manufacturer', 'type', 'u_height', 'starting_unit', 'width', 'outer_width',
'mounting_depth', 'weight', 'max_weight', 'description', 'comments', 'tags', 'created', 'last_updated', 'outer_depth', 'mounting_depth', 'weight', 'max_weight', 'description', 'comments', 'tags', 'created',
'last_updated',
) )
default_columns = ( default_columns = (
'pk', 'name', 'type', 'u_height', 'description', 'pk', 'name', 'manufacturer', 'type', 'u_height', 'description',
) )

View File

@ -276,33 +276,41 @@ class RackRoleTest(APIViewTestCases.APIViewTestCase):
class RackTypeTest(APIViewTestCases.APIViewTestCase): class RackTypeTest(APIViewTestCases.APIViewTestCase):
model = RackType model = RackType
brief_fields = ['description', 'display', 'id', 'name', 'slug', 'url'] brief_fields = ['description', 'display', 'id', 'manufacturer', 'name', 'slug', 'url']
bulk_update_data = { bulk_update_data = {
'description': 'new description', 'description': 'new description',
} }
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
manufacturers = (
racks = ( Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
RackType(name='RackType 1', slug='rack-type-1'), Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
RackType(name='RackType 2', slug='rack-type-2'),
RackType(name='RackType 3', slug='rack-type-3'),
) )
RackType.objects.bulk_create(racks) Manufacturer.objects.bulk_create(manufacturers)
rack_types = (
RackType(manufacturer=manufacturers[0], name='Rack Type 1', slug='rack-type-1'),
RackType(manufacturer=manufacturers[0], name='Rack Type 2', slug='rack-type-2'),
RackType(manufacturer=manufacturers[0], name='Rack Type 3', slug='rack-type-3'),
)
RackType.objects.bulk_create(rack_types)
cls.create_data = [ cls.create_data = [
{ {
'name': 'Test RackType 4', 'manufacturer': manufacturers[1].pk,
'slug': 'test-rack-type-4', 'name': 'Rack Type 4',
'slug': 'rack-type-4',
}, },
{ {
'name': 'Test RackType 5', 'manufacturer': manufacturers[1].pk,
'slug': 'test-rack-type-5', 'name': 'Rack Type 5',
'slug': 'rack-type-5',
}, },
{ {
'name': 'Test RackType 6', 'manufacturer': manufacturers[1].pk,
'slug': 'test-rack-type-6', 'name': 'Rack Type 6',
'slug': 'rack-type-6',
}, },
] ]

View File

@ -474,9 +474,16 @@ class RackTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
manufacturers = (
Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
Manufacturer(name='Manufacturer 3', slug='manufacturer-3'),
)
Manufacturer.objects.bulk_create(manufacturers)
racks = ( racks = (
RackType( RackType(
manufacturer=manufacturers[0],
name='RackType 1', name='RackType 1',
slug='rack-type-1', slug='rack-type-1',
type=RackTypeChoices.TYPE_2POST, type=RackTypeChoices.TYPE_2POST,
@ -492,6 +499,7 @@ class RackTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
description='foobar1' description='foobar1'
), ),
RackType( RackType(
manufacturer=manufacturers[1],
name='RackType 2', name='RackType 2',
slug='rack-type-2', slug='rack-type-2',
type=RackTypeChoices.TYPE_4POST, type=RackTypeChoices.TYPE_4POST,
@ -507,6 +515,7 @@ class RackTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
description='foobar2' description='foobar2'
), ),
RackType( RackType(
manufacturer=manufacturers[2],
name='RackType 3', name='RackType 3',
slug='rack-type-3', slug='rack-type-3',
type=RackTypeChoices.TYPE_CABINET, type=RackTypeChoices.TYPE_CABINET,
@ -528,6 +537,13 @@ class RackTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'q': 'foobar1'} params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
def test_manufacturer(self):
manufacturers = Manufacturer.objects.all()[:2]
params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'manufacturer': [manufacturers[0].slug, manufacturers[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self): def test_name(self):
params = {'name': ['RackType 1', 'RackType 2']} params = {'name': ['RackType 1', 'RackType 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

View File

@ -78,8 +78,10 @@ class RackTypeTestCase(TestCase):
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
RackType.objects.create( RackType.objects.create(
manufacturer=manufacturer,
name='RackType 1', name='RackType 1',
slug='rack-type-1', slug='rack-type-1',
width=11, width=11,

View File

@ -341,17 +341,23 @@ class RackTypeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
manufacturers = (
racks = ( Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
RackType(name='RackType 1', slug='rack-type-1',), Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
RackType(name='RackType 2', slug='rack-type-2',),
RackType(name='RackType 3', slug='rack-type-3',),
) )
RackType.objects.bulk_create(racks) Manufacturer.objects.bulk_create(manufacturers)
rack_types = (
RackType(manufacturer=manufacturers[0], name='RackType 1', slug='rack-type-1',),
RackType(manufacturer=manufacturers[0], name='RackType 2', slug='rack-type-2',),
RackType(manufacturer=manufacturers[0], name='RackType 3', slug='rack-type-3',),
)
RackType.objects.bulk_create(rack_types)
tags = create_tags('Alpha', 'Bravo', 'Charlie') tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = { cls.form_data = {
'manufacturer': manufacturers[1].pk,
'name': 'RackType X', 'name': 'RackType X',
'slug': 'rack-type-x', 'slug': 'rack-type-x',
'type': RackTypeChoices.TYPE_CABINET, 'type': RackTypeChoices.TYPE_CABINET,
@ -370,20 +376,21 @@ class RackTypeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
} }
cls.csv_data = ( cls.csv_data = (
"name,slug,width,u_height,weight,max_weight,weight_unit", "manufacturer,name,slug,width,u_height,weight,max_weight,weight_unit",
"RackType 4,rack-type-4,19,42,100,2000,kg", "Manufacturer 1,RackType 4,rack-type-4,19,42,100,2000,kg",
"RackType 5,rack-type-5,19,42,100,2000,kg", "Manufacturer 1,RackType 5,rack-type-5,19,42,100,2000,kg",
"RackType 6,rack-type-6,19,42,100,2000,kg", "Manufacturer 1,RackType 6,rack-type-6,19,42,100,2000,kg",
) )
cls.csv_update_data = ( cls.csv_update_data = (
"id,name", "id,name",
f"{racks[0].pk},RackType 7", f"{rack_types[0].pk},RackType 7",
f"{racks[1].pk},RackType 8", f"{rack_types[1].pk},RackType 8",
f"{racks[2].pk},RackType 9", f"{rack_types[2].pk},RackType 9",
) )
cls.bulk_edit_data = { cls.bulk_edit_data = {
'manufacturer': manufacturers[1].pk,
'type': RackTypeChoices.TYPE_4POST, 'type': RackTypeChoices.TYPE_4POST,
'width': RackWidthChoices.WIDTH_23IN, 'width': RackWidthChoices.WIDTH_23IN,
'u_height': 49, 'u_height': 49,

View File

@ -12,6 +12,10 @@
<div class="card"> <div class="card">
<h5 class="card-header">{% trans "Rack Type" %}</h5> <h5 class="card-header">{% trans "Rack Type" %}</h5>
<table class="table table-hover attr-table"> <table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Manufacturer" %}</th>
<td>{{ object.manufacturer|linkify }}</td>
</tr>
<tr> <tr>
<th scope="row">{% trans "Name" %}</th> <th scope="row">{% trans "Name" %}</th>
<td>{{ object.name }}</td> <td>{{ object.name }}</td>