Skip to content

Commit 944bda5

Browse files
kkthxbye-codejeremystretch
authored andcommitted
Fixes #9653 - Add default_platform to DeviceType
1 parent fa6cad1 commit 944bda5

12 files changed

+108
-13
lines changed

netbox/dcim/api/serializers.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ class Meta:
309309
class DeviceTypeSerializer(NetBoxModelSerializer):
310310
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
311311
manufacturer = NestedManufacturerSerializer()
312+
default_platform = NestedPlatformSerializer(required=False, allow_null=True)
312313
u_height = serializers.DecimalField(
313314
max_digits=4,
314315
decimal_places=1,
@@ -324,7 +325,7 @@ class DeviceTypeSerializer(NetBoxModelSerializer):
324325
class Meta:
325326
model = DeviceType
326327
fields = [
327-
'id', 'url', 'display', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth',
328+
'id', 'url', 'display', 'manufacturer', 'default_platform', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth',
328329
'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image', 'description',
329330
'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count',
330331
]

netbox/dcim/filtersets.py

+10
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,16 @@ class DeviceTypeFilterSet(NetBoxModelFilterSet):
436436
to_field_name='slug',
437437
label=_('Manufacturer (slug)'),
438438
)
439+
default_platform_id = django_filters.ModelMultipleChoiceFilter(
440+
queryset=Platform.objects.all(),
441+
label=_('Default platform (ID)'),
442+
)
443+
default_platform = django_filters.ModelMultipleChoiceFilter(
444+
field_name='default_platform__slug',
445+
queryset=Platform.objects.all(),
446+
to_field_name='slug',
447+
label=_('Default platform (slug)'),
448+
)
439449
has_front_image = django_filters.BooleanFilter(
440450
label=_('Has a front image'),
441451
method='_has_front_image'

netbox/dcim/forms/bulk_edit.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,10 @@ class DeviceTypeBulkEditForm(NetBoxModelBulkEditForm):
374374
queryset=Manufacturer.objects.all(),
375375
required=False
376376
)
377+
default_platform = DynamicModelChoiceField(
378+
queryset=Platform.objects.all(),
379+
required=False
380+
)
377381
part_number = forms.CharField(
378382
required=False
379383
)
@@ -412,7 +416,7 @@ class DeviceTypeBulkEditForm(NetBoxModelBulkEditForm):
412416

413417
model = DeviceType
414418
fieldsets = (
415-
('Device Type', ('manufacturer', 'part_number', 'u_height', 'is_full_depth', 'airflow', 'description')),
419+
('Device Type', ('manufacturer', 'default_platform', 'part_number', 'u_height', 'is_full_depth', 'airflow', 'description')),
416420
('Weight', ('weight', 'weight_unit')),
417421
)
418422
nullable_fields = ('part_number', 'airflow', 'weight', 'weight_unit', 'description', 'comments')

netbox/dcim/forms/bulk_import.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -281,12 +281,17 @@ class DeviceTypeImportForm(NetBoxModelImportForm):
281281
queryset=Manufacturer.objects.all(),
282282
to_field_name='name'
283283
)
284+
default_platform = forms.ModelChoiceField(
285+
queryset=Platform.objects.all(),
286+
to_field_name='name',
287+
required=False,
288+
)
284289

285290
class Meta:
286291
model = DeviceType
287292
fields = [
288-
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
289-
'description', 'comments',
293+
'manufacturer', 'default_platform', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth',
294+
'subdevice_role', 'airflow', 'description', 'comments',
290295
]
291296

292297

netbox/dcim/forms/filtersets.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ class DeviceTypeFilterForm(NetBoxModelFilterSetForm):
378378
model = DeviceType
379379
fieldsets = (
380380
(None, ('q', 'filter_id', 'tag')),
381-
('Hardware', ('manufacturer_id', 'part_number', 'subdevice_role', 'airflow')),
381+
('Hardware', ('manufacturer_id', 'default_platform_id', 'part_number', 'subdevice_role', 'airflow')),
382382
('Images', ('has_front_image', 'has_rear_image')),
383383
('Components', (
384384
'console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces',
@@ -391,6 +391,11 @@ class DeviceTypeFilterForm(NetBoxModelFilterSetForm):
391391
required=False,
392392
label=_('Manufacturer')
393393
)
394+
default_platform_id = DynamicModelMultipleChoiceField(
395+
queryset=Platform.objects.all(),
396+
required=False,
397+
label=_('Default platform')
398+
)
394399
part_number = forms.CharField(
395400
required=False
396401
)

netbox/dcim/forms/model_forms.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -378,13 +378,17 @@ class DeviceTypeForm(NetBoxModelForm):
378378
manufacturer = DynamicModelChoiceField(
379379
queryset=Manufacturer.objects.all()
380380
)
381+
default_platform = DynamicModelChoiceField(
382+
queryset=Platform.objects.all(),
383+
required=False
384+
)
381385
slug = SlugField(
382386
slug_source='model'
383387
)
384388
comments = CommentField()
385389

386390
fieldsets = (
387-
('Device Type', ('manufacturer', 'model', 'slug', 'description', 'tags')),
391+
('Device Type', ('manufacturer', 'model', 'slug', 'description', 'tags', 'default_platform')),
388392
('Chassis', (
389393
'u_height', 'is_full_depth', 'part_number', 'subdevice_role', 'airflow', 'weight', 'weight_unit',
390394
)),
@@ -395,7 +399,7 @@ class Meta:
395399
model = DeviceType
396400
fields = [
397401
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
398-
'weight', 'weight_unit', 'front_image', 'rear_image', 'description', 'comments', 'tags',
402+
'weight', 'weight_unit', 'front_image', 'rear_image', 'description', 'comments', 'tags', 'default_platform'
399403
]
400404
widgets = {
401405
'airflow': StaticSelect(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Generated by Django 4.1.6 on 2023-02-10 18:06
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('dcim', '0168_interface_template_enabled'),
11+
]
12+
13+
operations = [
14+
migrations.AddField(
15+
model_name='devicetype',
16+
name='default_platform',
17+
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.platform'),
18+
),
19+
]

netbox/dcim/models/devices.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ class DeviceType(PrimaryModel, WeightMixin):
8282
slug = models.SlugField(
8383
max_length=100
8484
)
85+
default_platform = models.ForeignKey(
86+
to='dcim.Platform',
87+
on_delete=models.SET_NULL,
88+
related_name='+',
89+
blank=True,
90+
null=True,
91+
verbose_name='Default platform'
92+
)
8593
part_number = models.CharField(
8694
max_length=50,
8795
blank=True,
@@ -121,7 +129,7 @@ class DeviceType(PrimaryModel, WeightMixin):
121129
)
122130

123131
clone_fields = (
124-
'manufacturer', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit'
132+
'manufacturer', 'default_platform', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit'
125133
)
126134
prerequisite_models = (
127135
'dcim.Manufacturer',
@@ -165,6 +173,7 @@ def to_yaml(self):
165173
'manufacturer': self.manufacturer.name,
166174
'model': self.model,
167175
'slug': self.slug,
176+
'default_platform': self.default_platform.name if self.default_platform else None,
168177
'part_number': self.part_number,
169178
'u_height': float(self.u_height),
170179
'is_full_depth': self.is_full_depth,
@@ -801,6 +810,10 @@ def save(self, *args, **kwargs):
801810
if is_new and not self.airflow:
802811
self.airflow = self.device_type.airflow
803812

813+
# Inherit default_platform from DeviceType if not set
814+
if is_new and not self.platform:
815+
self.platform = self.device_type.default_platform
816+
804817
super().save(*args, **kwargs)
805818

806819
# If this is a new Device, instantiate all the related components per the DeviceType definition

netbox/dcim/tables/devicetypes.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ class DeviceTypeTable(NetBoxTable):
7777
manufacturer = tables.Column(
7878
linkify=True
7979
)
80+
default_platform = tables.Column(
81+
linkify=True
82+
)
8083
is_full_depth = columns.BooleanColumn(
8184
verbose_name='Full Depth'
8285
)
@@ -100,7 +103,7 @@ class DeviceTypeTable(NetBoxTable):
100103
class Meta(NetBoxTable.Meta):
101104
model = models.DeviceType
102105
fields = (
103-
'pk', 'id', 'model', 'manufacturer', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
106+
'pk', 'id', 'model', 'manufacturer', 'default_platform', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
104107
'airflow', 'weight', 'description', 'comments', 'instance_count', 'tags', 'created', 'last_updated',
105108
)
106109
default_columns = (

netbox/dcim/tests/test_filtersets.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -699,9 +699,16 @@ def setUpTestData(cls):
699699
)
700700
Manufacturer.objects.bulk_create(manufacturers)
701701

702+
platforms = (
703+
Platform(name='Platform 1', slug='platform-1', manufacturer=manufacturers[0]),
704+
Platform(name='Platform 2', slug='platform-2', manufacturer=manufacturers[1]),
705+
Platform(name='Platform 3', slug='platform-3', manufacturer=manufacturers[2]),
706+
)
707+
Platform.objects.bulk_create(platforms)
708+
702709
device_types = (
703-
DeviceType(manufacturer=manufacturers[0], model='Model 1', slug='model-1', part_number='Part Number 1', u_height=1, is_full_depth=True, front_image='front.png', rear_image='rear.png', weight=10, weight_unit=WeightUnitChoices.UNIT_POUND),
704-
DeviceType(manufacturer=manufacturers[1], model='Model 2', slug='model-2', part_number='Part Number 2', u_height=2, is_full_depth=True, subdevice_role=SubdeviceRoleChoices.ROLE_PARENT, airflow=DeviceAirflowChoices.AIRFLOW_FRONT_TO_REAR, weight=20, weight_unit=WeightUnitChoices.UNIT_POUND),
710+
DeviceType(manufacturer=manufacturers[0], default_platform=platforms[0], model='Model 1', slug='model-1', part_number='Part Number 1', u_height=1, is_full_depth=True, front_image='front.png', rear_image='rear.png', weight=10, weight_unit=WeightUnitChoices.UNIT_POUND),
711+
DeviceType(manufacturer=manufacturers[1], default_platform=platforms[1], model='Model 2', slug='model-2', part_number='Part Number 2', u_height=2, is_full_depth=True, subdevice_role=SubdeviceRoleChoices.ROLE_PARENT, airflow=DeviceAirflowChoices.AIRFLOW_FRONT_TO_REAR, weight=20, weight_unit=WeightUnitChoices.UNIT_POUND),
705712
DeviceType(manufacturer=manufacturers[2], model='Model 3', slug='model-3', part_number='Part Number 3', u_height=3, is_full_depth=False, subdevice_role=SubdeviceRoleChoices.ROLE_CHILD, airflow=DeviceAirflowChoices.AIRFLOW_REAR_TO_FRONT, weight=30, weight_unit=WeightUnitChoices.UNIT_KILOGRAM),
706713
)
707714
DeviceType.objects.bulk_create(device_types)
@@ -785,6 +792,13 @@ def test_manufacturer(self):
785792
params = {'manufacturer': [manufacturers[0].slug, manufacturers[1].slug]}
786793
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
787794

795+
def test_default_platform(self):
796+
platforms = Platform.objects.all()[:2]
797+
params = {'default_platform_id': [platforms[0].pk, platforms[1].pk]}
798+
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
799+
params = {'default_platform': [platforms[0].slug, platforms[1].slug]}
800+
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
801+
788802
def test_has_front_image(self):
789803
params = {'has_front_image': True}
790804
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)

netbox/dcim/tests/test_views.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,12 @@ def setUpTestData(cls):
503503
)
504504
Manufacturer.objects.bulk_create(manufacturers)
505505

506+
platforms = (
507+
Platform(name='Platform 1', slug='platform-1', manufacturer=manufacturers[0]),
508+
Platform(name='Platform 2', slug='platform-3', manufacturer=manufacturers[1]),
509+
)
510+
Platform.objects.bulk_create(platforms)
511+
506512
DeviceType.objects.bulk_create([
507513
DeviceType(model='Device Type 1', slug='device-type-1', manufacturer=manufacturers[0]),
508514
DeviceType(model='Device Type 2', slug='device-type-2', manufacturer=manufacturers[0]),
@@ -513,6 +519,7 @@ def setUpTestData(cls):
513519

514520
cls.form_data = {
515521
'manufacturer': manufacturers[1].pk,
522+
'default_platform': platforms[0].pk,
516523
'model': 'Device Type X',
517524
'slug': 'device-type-x',
518525
'part_number': '123ABC',
@@ -525,6 +532,7 @@ def setUpTestData(cls):
525532

526533
cls.bulk_edit_data = {
527534
'manufacturer': manufacturers[1].pk,
535+
'default_platform': platforms[1].pk,
528536
'u_height': 3,
529537
'is_full_depth': False,
530538
}
@@ -673,6 +681,7 @@ def test_import_objects(self):
673681
"""
674682
IMPORT_DATA = """
675683
manufacturer: Generic
684+
default_platform: Platform
676685
model: TEST-1000
677686
slug: test-1000
678687
u_height: 2
@@ -755,8 +764,11 @@ def test_import_objects(self):
755764
manufacturer: Generic
756765
"""
757766

758-
# Create the manufacturer
759-
Manufacturer(name='Generic', slug='generic').save()
767+
# Create the manufacturer and platform
768+
manufacturer = Manufacturer(name='Generic', slug='generic')
769+
manufacturer.save()
770+
platform = Platform(name='Platform', slug='test-platform', manufacturer=manufacturer)
771+
platform.save()
760772

761773
# Add all required permissions to the test user
762774
self.add_permissions(
@@ -783,6 +795,7 @@ def test_import_objects(self):
783795

784796
device_type = DeviceType.objects.get(model='TEST-1000')
785797
self.assertEqual(device_type.comments, 'Test comment')
798+
self.assertEqual(device_type.default_platform.pk, platform.pk)
786799

787800
# Verify all of the components were created
788801
self.assertEqual(device_type.consoleporttemplates.count(), 3)

netbox/templates/dcim/devicetype.html

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ <h5 class="card-header">
2727
<td>Part Number</td>
2828
<td>{{ object.part_number|placeholder }}</td>
2929
</tr>
30+
<tr>
31+
<td>Default Platform</td>
32+
<td>{{ object.default_platform|linkify }}</td>
33+
</tr>
3034
<tr>
3135
<td>Description</td>
3236
<td>{{ object.description|placeholder }}</td>

0 commit comments

Comments
 (0)