diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index 449a96dcc..fa0af7228 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -384,28 +384,28 @@ class DeviceTypeFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFil ) def _console_ports(self, queryset, name, value): - return queryset.exclude(consoleport_templates__isnull=value) + return queryset.exclude(consoleporttemplates__isnull=value) def _console_server_ports(self, queryset, name, value): - return queryset.exclude(consoleserverport_templates__isnull=value) + return queryset.exclude(consoleserverporttemplates__isnull=value) def _power_ports(self, queryset, name, value): - return queryset.exclude(powerport_templates__isnull=value) + return queryset.exclude(powerporttemplates__isnull=value) def _power_outlets(self, queryset, name, value): - return queryset.exclude(poweroutlet_templates__isnull=value) + return queryset.exclude(poweroutlettemplates__isnull=value) def _interfaces(self, queryset, name, value): - return queryset.exclude(interface_templates__isnull=value) + return queryset.exclude(interfacetemplates__isnull=value) def _pass_through_ports(self, queryset, name, value): return queryset.exclude( - frontport_templates__isnull=value, - rearport_templates__isnull=value + frontporttemplates__isnull=value, + rearporttemplates__isnull=value ) def _device_bays(self, queryset, name, value): - return queryset.exclude(device_bay_templates__isnull=value) + return queryset.exclude(devicebaytemplates__isnull=value) class DeviceTypeComponentFilterSet(NameSlugSearchFilterSet): @@ -656,7 +656,7 @@ class DeviceFilterSet( return queryset.filter( Q(name__icontains=value) | Q(serial__icontains=value.strip()) | - Q(inventory_items__serial__icontains=value.strip()) | + Q(inventoryitems__serial__icontains=value.strip()) | Q(asset_tag__icontains=value.strip()) | Q(comments__icontains=value) ).distinct() @@ -698,7 +698,7 @@ class DeviceFilterSet( ) def _device_bays(self, queryset, name, value): - return queryset.exclude(device_bays__isnull=value) + return queryset.exclude(devicebays__isnull=value) class DeviceComponentFilterSet(django_filters.FilterSet): diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 65cce8850..a35835732 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -1392,7 +1392,7 @@ class FrontPortTemplateCreateForm(ComponentTemplateCreateForm): # Determine which rear port positions are occupied. These will be excluded from the list of available mappings. occupied_port_positions = [ (front_port.rear_port_id, front_port.rear_port_position) - for front_port in device_type.frontport_templates.all() + for front_port in device_type.frontporttemplates.all() ] # Populate rear port choices diff --git a/netbox/dcim/migrations/0112_standardize_component_name.py b/netbox/dcim/migrations/0112_standardize_component_name.py deleted file mode 100644 index 1e12b1bf2..000000000 --- a/netbox/dcim/migrations/0112_standardize_component_name.py +++ /dev/null @@ -1,68 +0,0 @@ -# Generated by Django 3.0.6 on 2020-07-02 16:02 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('dcim', '0111_component_template_description'), - ] - - operations = [ - migrations.AlterField( - model_name='consoleport', - name='name', - field=models.CharField(max_length=64), - ), - migrations.AlterField( - model_name='consoleporttemplate', - name='name', - field=models.CharField(max_length=64), - ), - migrations.AlterField( - model_name='consoleserverport', - name='name', - field=models.CharField(max_length=64), - ), - migrations.AlterField( - model_name='consoleserverporttemplate', - name='name', - field=models.CharField(max_length=64), - ), - migrations.AlterField( - model_name='devicebay', - name='name', - field=models.CharField(max_length=64), - ), - migrations.AlterField( - model_name='devicebaytemplate', - name='name', - field=models.CharField(max_length=64), - ), - migrations.AlterField( - model_name='inventoryitem', - name='name', - field=models.CharField(max_length=64), - ), - migrations.AlterField( - model_name='poweroutlet', - name='name', - field=models.CharField(max_length=64), - ), - migrations.AlterField( - model_name='poweroutlettemplate', - name='name', - field=models.CharField(max_length=64), - ), - migrations.AlterField( - model_name='powerport', - name='name', - field=models.CharField(max_length=64), - ), - migrations.AlterField( - model_name='powerporttemplate', - name='name', - field=models.CharField(max_length=64), - ), - ] diff --git a/netbox/dcim/migrations/0112_standardize_components.py b/netbox/dcim/migrations/0112_standardize_components.py new file mode 100644 index 000000000..1a3465e02 --- /dev/null +++ b/netbox/dcim/migrations/0112_standardize_components.py @@ -0,0 +1,120 @@ +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0111_component_template_description'), + ] + + operations = [ + # Set max_length=64 for all name fields + migrations.AlterField( + model_name='consoleport', + name='name', + field=models.CharField(max_length=64), + ), + migrations.AlterField( + model_name='consoleporttemplate', + name='name', + field=models.CharField(max_length=64), + ), + migrations.AlterField( + model_name='consoleserverport', + name='name', + field=models.CharField(max_length=64), + ), + migrations.AlterField( + model_name='consoleserverporttemplate', + name='name', + field=models.CharField(max_length=64), + ), + migrations.AlterField( + model_name='devicebay', + name='name', + field=models.CharField(max_length=64), + ), + migrations.AlterField( + model_name='devicebaytemplate', + name='name', + field=models.CharField(max_length=64), + ), + migrations.AlterField( + model_name='inventoryitem', + name='name', + field=models.CharField(max_length=64), + ), + migrations.AlterField( + model_name='poweroutlet', + name='name', + field=models.CharField(max_length=64), + ), + migrations.AlterField( + model_name='poweroutlettemplate', + name='name', + field=models.CharField(max_length=64), + ), + migrations.AlterField( + model_name='powerport', + name='name', + field=models.CharField(max_length=64), + ), + migrations.AlterField( + model_name='powerporttemplate', + name='name', + field=models.CharField(max_length=64), + ), + + # Update related_name for necessary component and component template models + migrations.AlterField( + model_name='consoleporttemplate', + name='device_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='consoleporttemplates', to='dcim.DeviceType'), + ), + migrations.AlterField( + model_name='consoleserverporttemplate', + name='device_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='consoleserverporttemplates', to='dcim.DeviceType'), + ), + migrations.AlterField( + model_name='devicebay', + name='device', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='devicebays', to='dcim.Device'), + ), + migrations.AlterField( + model_name='devicebaytemplate', + name='device_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='devicebaytemplates', to='dcim.DeviceType'), + ), + migrations.AlterField( + model_name='frontporttemplate', + name='device_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frontporttemplates', to='dcim.DeviceType'), + ), + migrations.AlterField( + model_name='interfacetemplate', + name='device_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interfacetemplates', to='dcim.DeviceType'), + ), + migrations.AlterField( + model_name='inventoryitem', + name='device', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='inventoryitems', to='dcim.Device'), + ), + migrations.AlterField( + model_name='poweroutlettemplate', + name='device_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='poweroutlettemplates', to='dcim.DeviceType'), + ), + migrations.AlterField( + model_name='powerporttemplate', + name='device_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='powerporttemplates', to='dcim.DeviceType'), + ), + migrations.AlterField( + model_name='rearporttemplate', + name='device_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rearporttemplates', to='dcim.DeviceType'), + ), + ] diff --git a/netbox/dcim/models/__init__.py b/netbox/dcim/models/__init__.py index 1aeedf1e5..db8fd8cf3 100644 --- a/netbox/dcim/models/__init__.py +++ b/netbox/dcim/models/__init__.py @@ -678,7 +678,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel): 'device_type__manufacturer', 'device_role' ).annotate( - devicebay_count=Count('device_bays') + devicebay_count=Count('devicebays') ).exclude( pk=exclude ).filter( @@ -1049,23 +1049,23 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel): )) # Component templates - if self.consoleport_templates.exists(): + if self.consoleporttemplates.exists(): data['console-ports'] = [ { 'name': c.name, 'type': c.type, } - for c in self.consoleport_templates.all() + for c in self.consoleporttemplates.all() ] - if self.consoleserverport_templates.exists(): + if self.consoleserverporttemplates.exists(): data['console-server-ports'] = [ { 'name': c.name, 'type': c.type, } - for c in self.consoleserverport_templates.all() + for c in self.consoleserverporttemplates.all() ] - if self.powerport_templates.exists(): + if self.powerporttemplates.exists(): data['power-ports'] = [ { 'name': c.name, @@ -1073,9 +1073,9 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel): 'maximum_draw': c.maximum_draw, 'allocated_draw': c.allocated_draw, } - for c in self.powerport_templates.all() + for c in self.powerporttemplates.all() ] - if self.poweroutlet_templates.exists(): + if self.poweroutlettemplates.exists(): data['power-outlets'] = [ { 'name': c.name, @@ -1083,18 +1083,18 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel): 'power_port': c.power_port.name if c.power_port else None, 'feed_leg': c.feed_leg, } - for c in self.poweroutlet_templates.all() + for c in self.poweroutlettemplates.all() ] - if self.interface_templates.exists(): + if self.interfacetemplates.exists(): data['interfaces'] = [ { 'name': c.name, 'type': c.type, 'mgmt_only': c.mgmt_only, } - for c in self.interface_templates.all() + for c in self.interfacetemplates.all() ] - if self.frontport_templates.exists(): + if self.frontporttemplates.exists(): data['front-ports'] = [ { 'name': c.name, @@ -1102,23 +1102,23 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel): 'rear_port': c.rear_port.name, 'rear_port_position': c.rear_port_position, } - for c in self.frontport_templates.all() + for c in self.frontporttemplates.all() ] - if self.rearport_templates.exists(): + if self.rearporttemplates.exists(): data['rear-ports'] = [ { 'name': c.name, 'type': c.type, 'positions': c.positions, } - for c in self.rearport_templates.all() + for c in self.rearporttemplates.all() ] - if self.device_bay_templates.exists(): + if self.devicebaytemplates.exists(): data['device-bays'] = [ { 'name': c.name, } - for c in self.device_bay_templates.all() + for c in self.devicebaytemplates.all() ] return yaml.dump(dict(data), sort_keys=False) @@ -1159,7 +1159,7 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel): if ( self.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT - ) and self.device_bay_templates.count(): + ) and self.devicebaytemplates.count(): raise ValidationError({ 'subdevice_role': "Must delete all device bay templates associated with this device before " "declassifying it as a parent device." @@ -1634,28 +1634,28 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel): # If this is a new Device, instantiate all of the related components per the DeviceType definition if is_new: ConsolePort.objects.bulk_create( - [x.instantiate(self) for x in self.device_type.consoleport_templates.unrestricted()] + [x.instantiate(self) for x in self.device_type.consoleporttemplates.unrestricted()] ) ConsoleServerPort.objects.bulk_create( - [x.instantiate(self) for x in self.device_type.consoleserverport_templates.unrestricted()] + [x.instantiate(self) for x in self.device_type.consoleserverporttemplates.unrestricted()] ) PowerPort.objects.bulk_create( - [x.instantiate(self) for x in self.device_type.powerport_templates.unrestricted()] + [x.instantiate(self) for x in self.device_type.powerporttemplates.unrestricted()] ) PowerOutlet.objects.bulk_create( - [x.instantiate(self) for x in self.device_type.poweroutlet_templates.unrestricted()] + [x.instantiate(self) for x in self.device_type.poweroutlettemplates.unrestricted()] ) Interface.objects.bulk_create( - [x.instantiate(self) for x in self.device_type.interface_templates.unrestricted()] + [x.instantiate(self) for x in self.device_type.interfacetemplates.unrestricted()] ) RearPort.objects.bulk_create( - [x.instantiate(self) for x in self.device_type.rearport_templates.unrestricted()] + [x.instantiate(self) for x in self.device_type.rearporttemplates.unrestricted()] ) FrontPort.objects.bulk_create( - [x.instantiate(self) for x in self.device_type.frontport_templates.unrestricted()] + [x.instantiate(self) for x in self.device_type.frontporttemplates.unrestricted()] ) DeviceBay.objects.bulk_create( - [x.instantiate(self) for x in self.device_type.device_bay_templates.unrestricted()] + [x.instantiate(self) for x in self.device_type.devicebaytemplates.unrestricted()] ) # Update Site and Rack assignment for any child Devices diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index a7b76fdde..7e96a8016 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -27,6 +27,11 @@ __all__ = ( class ComponentTemplateModel(models.Model): + device_type = models.ForeignKey( + to='dcim.DeviceType', + on_delete=models.CASCADE, + related_name='%(class)ss' + ) name = models.CharField( max_length=64 ) @@ -81,11 +86,6 @@ class ConsolePortTemplate(ComponentTemplateModel): """ A template for a ConsolePort to be created for a new Device. """ - device_type = models.ForeignKey( - to='dcim.DeviceType', - on_delete=models.CASCADE, - related_name='consoleport_templates' - ) type = models.CharField( max_length=50, choices=ConsolePortTypeChoices, @@ -108,11 +108,6 @@ class ConsoleServerPortTemplate(ComponentTemplateModel): """ A template for a ConsoleServerPort to be created for a new Device. """ - device_type = models.ForeignKey( - to='dcim.DeviceType', - on_delete=models.CASCADE, - related_name='consoleserverport_templates' - ) type = models.CharField( max_length=50, choices=ConsolePortTypeChoices, @@ -135,11 +130,6 @@ class PowerPortTemplate(ComponentTemplateModel): """ A template for a PowerPort to be created for a new Device. """ - device_type = models.ForeignKey( - to='dcim.DeviceType', - on_delete=models.CASCADE, - related_name='powerport_templates' - ) type = models.CharField( max_length=50, choices=PowerPortTypeChoices, @@ -176,11 +166,6 @@ class PowerOutletTemplate(ComponentTemplateModel): """ A template for a PowerOutlet to be created for a new Device. """ - device_type = models.ForeignKey( - to='dcim.DeviceType', - on_delete=models.CASCADE, - related_name='poweroutlet_templates' - ) type = models.CharField( max_length=50, choices=PowerOutletTypeChoices, @@ -230,11 +215,7 @@ class InterfaceTemplate(ComponentTemplateModel): """ A template for a physical data interface on a new Device. """ - device_type = models.ForeignKey( - to='dcim.DeviceType', - on_delete=models.CASCADE, - related_name='interface_templates' - ) + # Override ComponentTemplateModel._name to specify naturalize_interface function _name = NaturalOrderingField( target_field='name', naturalize_function=naturalize_interface, @@ -267,11 +248,6 @@ class FrontPortTemplate(ComponentTemplateModel): """ Template for a pass-through port on the front of a new Device. """ - device_type = models.ForeignKey( - to='dcim.DeviceType', - on_delete=models.CASCADE, - related_name='frontport_templates' - ) type = models.CharField( max_length=50, choices=PortTypeChoices @@ -327,11 +303,6 @@ class RearPortTemplate(ComponentTemplateModel): """ Template for a pass-through port on the rear of a new Device. """ - device_type = models.ForeignKey( - to='dcim.DeviceType', - on_delete=models.CASCADE, - related_name='rearport_templates' - ) type = models.CharField( max_length=50, choices=PortTypeChoices @@ -358,12 +329,6 @@ class DeviceBayTemplate(ComponentTemplateModel): """ A template for a DeviceBay to be created for a new parent Device. """ - device_type = models.ForeignKey( - to='dcim.DeviceType', - on_delete=models.CASCADE, - related_name='device_bay_templates' - ) - class Meta: ordering = ('device_type', '_name') unique_together = ('device_type', 'name') diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 6a2591916..1ebb0c4d5 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -36,6 +36,11 @@ __all__ = ( class ComponentModel(models.Model): + device = models.ForeignKey( + to='dcim.Device', + on_delete=models.CASCADE, + related_name='%(class)ss' + ) name = models.CharField( max_length=64 ) @@ -246,11 +251,6 @@ class ConsolePort(CableTermination, ComponentModel): """ A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts. """ - device = models.ForeignKey( - to='dcim.Device', - on_delete=models.CASCADE, - related_name='consoleports' - ) type = models.CharField( max_length=50, choices=ConsolePortTypeChoices, @@ -298,11 +298,6 @@ class ConsoleServerPort(CableTermination, ComponentModel): """ A physical port within a Device (typically a designated console server) which provides access to ConsolePorts. """ - device = models.ForeignKey( - to='dcim.Device', - on_delete=models.CASCADE, - related_name='consoleserverports' - ) type = models.CharField( max_length=50, choices=ConsolePortTypeChoices, @@ -343,11 +338,6 @@ class PowerPort(CableTermination, ComponentModel): """ A physical power supply (intake) port within a Device. PowerPorts connect to PowerOutlets. """ - device = models.ForeignKey( - to='dcim.Device', - on_delete=models.CASCADE, - related_name='powerports' - ) type = models.CharField( max_length=50, choices=PowerPortTypeChoices, @@ -496,11 +486,6 @@ class PowerOutlet(CableTermination, ComponentModel): """ A physical power outlet (output) within a Device which provides power to a PowerPort. """ - device = models.ForeignKey( - to='dcim.Device', - on_delete=models.CASCADE, - related_name='poweroutlets' - ) type = models.CharField( max_length=50, choices=PowerOutletTypeChoices, @@ -560,6 +545,9 @@ class PowerOutlet(CableTermination, ComponentModel): # class BaseInterface(models.Model): + """ + Abstract base class for fields shared by dcim.Interface and virtualization.VMInterface. + """ enabled = models.BooleanField( default=True ) @@ -589,13 +577,7 @@ class Interface(CableTermination, ComponentModel, BaseInterface): """ A network interface within a Device. A physical Interface can connect to exactly one other Interface. """ - device = models.ForeignKey( - to='Device', - on_delete=models.CASCADE, - related_name='interfaces', - null=True, - blank=True - ) + # Override ComponentModel._name to specify naturalize_interface function _name = NaturalOrderingField( target_field='name', naturalize_function=naturalize_interface, @@ -807,11 +789,6 @@ class FrontPort(CableTermination, ComponentModel): """ A pass-through port on the front of a Device. """ - device = models.ForeignKey( - to='dcim.Device', - on_delete=models.CASCADE, - related_name='frontports' - ) type = models.CharField( max_length=50, choices=PortTypeChoices @@ -872,11 +849,6 @@ class RearPort(CableTermination, ComponentModel): """ A pass-through port on the rear of a Device. """ - device = models.ForeignKey( - to='dcim.Device', - on_delete=models.CASCADE, - related_name='rearports' - ) type = models.CharField( max_length=50, choices=PortTypeChoices @@ -916,11 +888,6 @@ class DeviceBay(ComponentModel): """ An empty space within a Device which can house a child device """ - device = models.ForeignKey( - to='dcim.Device', - on_delete=models.CASCADE, - related_name='device_bays' - ) installed_device = models.OneToOneField( to='dcim.Device', on_delete=models.SET_NULL, @@ -981,11 +948,6 @@ class InventoryItem(ComponentModel): An InventoryItem represents a serialized piece of hardware within a Device, such as a line card or power supply. InventoryItems are used only for inventory purposes. """ - device = models.ForeignKey( - to='dcim.Device', - on_delete=models.CASCADE, - related_name='inventory_items' - ) parent = models.ForeignKey( to='self', on_delete=models.CASCADE, diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py index e1baadcc1..ef7ff993f 100644 --- a/netbox/dcim/tests/test_views.py +++ b/netbox/dcim/tests/test_views.py @@ -481,45 +481,45 @@ device-bays: self.assertEqual(dt.comments, 'test comment') # Verify all of the components were created - self.assertEqual(dt.consoleport_templates.count(), 3) + self.assertEqual(dt.consoleporttemplates.count(), 3) cp1 = ConsolePortTemplate.objects.first() self.assertEqual(cp1.name, 'Console Port 1') self.assertEqual(cp1.type, ConsolePortTypeChoices.TYPE_DE9) - self.assertEqual(dt.consoleserverport_templates.count(), 3) + self.assertEqual(dt.consoleserverporttemplates.count(), 3) csp1 = ConsoleServerPortTemplate.objects.first() self.assertEqual(csp1.name, 'Console Server Port 1') self.assertEqual(csp1.type, ConsolePortTypeChoices.TYPE_RJ45) - self.assertEqual(dt.powerport_templates.count(), 3) + self.assertEqual(dt.powerporttemplates.count(), 3) pp1 = PowerPortTemplate.objects.first() self.assertEqual(pp1.name, 'Power Port 1') self.assertEqual(pp1.type, PowerPortTypeChoices.TYPE_IEC_C14) - self.assertEqual(dt.poweroutlet_templates.count(), 3) + self.assertEqual(dt.poweroutlettemplates.count(), 3) po1 = PowerOutletTemplate.objects.first() self.assertEqual(po1.name, 'Power Outlet 1') self.assertEqual(po1.type, PowerOutletTypeChoices.TYPE_IEC_C13) self.assertEqual(po1.power_port, pp1) self.assertEqual(po1.feed_leg, PowerOutletFeedLegChoices.FEED_LEG_A) - self.assertEqual(dt.interface_templates.count(), 3) + self.assertEqual(dt.interfacetemplates.count(), 3) iface1 = InterfaceTemplate.objects.first() self.assertEqual(iface1.name, 'Interface 1') self.assertEqual(iface1.type, InterfaceTypeChoices.TYPE_1GE_FIXED) self.assertTrue(iface1.mgmt_only) - self.assertEqual(dt.rearport_templates.count(), 3) + self.assertEqual(dt.rearporttemplates.count(), 3) rp1 = RearPortTemplate.objects.first() self.assertEqual(rp1.name, 'Rear Port 1') - self.assertEqual(dt.frontport_templates.count(), 3) + self.assertEqual(dt.frontporttemplates.count(), 3) fp1 = FrontPortTemplate.objects.first() self.assertEqual(fp1.name, 'Front Port 1') self.assertEqual(fp1.rear_port, rp1) self.assertEqual(fp1.rear_port_position, 1) - self.assertEqual(dt.device_bay_templates.count(), 3) + self.assertEqual(dt.devicebaytemplates.count(), 3) db1 = DeviceBayTemplate.objects.first() self.assertEqual(db1.name, 'Device Bay 1') diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 01f125db4..408d0d833 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -101,7 +101,7 @@