From f51415cf2c581b63bad0807b5d32f4d5c0231af9 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 27 Sep 2022 15:35:24 -0400 Subject: [PATCH] Replace unique_together with UniqueConstraints --- .../migrations/0039_unique_constraints.py | 39 +++ netbox/circuits/models/circuits.py | 14 +- netbox/circuits/models/providers.py | 3 +- .../migrations/0162_unique_constraints.py | 246 +++++++++++++++++- .../dcim/models/device_component_templates.py | 95 +++---- netbox/dcim/models/device_components.py | 62 ++--- netbox/dcim/models/devices.py | 21 +- netbox/dcim/models/power.py | 14 +- netbox/dcim/models/racks.py | 17 +- .../migrations/0078_unique_constraints.py | 27 ++ netbox/extras/models/models.py | 16 +- .../migrations/0062_unique_constraints.py | 43 +++ netbox/ipam/models/fhrp.py | 7 +- netbox/ipam/models/vlans.py | 28 +- .../migrations/0008_unique_constraints.py | 35 +++ netbox/tenancy/models/contacts.py | 21 +- .../migrations/0033_unique_constraints.py | 22 +- netbox/virtualization/models.py | 21 +- .../migrations/0006_unique_constraints.py | 27 ++ netbox/wireless/models.py | 14 +- 20 files changed, 630 insertions(+), 142 deletions(-) create mode 100644 netbox/circuits/migrations/0039_unique_constraints.py create mode 100644 netbox/extras/migrations/0078_unique_constraints.py create mode 100644 netbox/ipam/migrations/0062_unique_constraints.py create mode 100644 netbox/tenancy/migrations/0008_unique_constraints.py create mode 100644 netbox/wireless/migrations/0006_unique_constraints.py diff --git a/netbox/circuits/migrations/0039_unique_constraints.py b/netbox/circuits/migrations/0039_unique_constraints.py new file mode 100644 index 000000000..1d5b62499 --- /dev/null +++ b/netbox/circuits/migrations/0039_unique_constraints.py @@ -0,0 +1,39 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('circuits', '0038_cabling_cleanup'), + ] + + operations = [ + migrations.RemoveConstraint( + model_name='providernetwork', + name='circuits_providernetwork_provider_name', + ), + migrations.AlterUniqueTogether( + name='circuit', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='circuittermination', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='providernetwork', + unique_together=set(), + ), + migrations.AddConstraint( + model_name='circuit', + constraint=models.UniqueConstraint(fields=('provider', 'cid'), name='circuits_circuit_unique_provider_cid'), + ), + migrations.AddConstraint( + model_name='circuittermination', + constraint=models.UniqueConstraint(fields=('circuit', 'term_side'), name='circuits_circuittermination_unique_circuit_term_side'), + ), + migrations.AddConstraint( + model_name='providernetwork', + constraint=models.UniqueConstraint(fields=('provider', 'name'), name='circuits_providernetwork_unique_provider_name'), + ), + ] diff --git a/netbox/circuits/models/circuits.py b/netbox/circuits/models/circuits.py index c08b5473a..ea74eeb40 100644 --- a/netbox/circuits/models/circuits.py +++ b/netbox/circuits/models/circuits.py @@ -132,7 +132,12 @@ class Circuit(NetBoxModel): class Meta: ordering = ['provider', 'cid'] - unique_together = ['provider', 'cid'] + constraints = ( + models.UniqueConstraint( + fields=('provider', 'cid'), + name='%(app_label)s_%(class)s_unique_provider_cid' + ), + ) def __str__(self): return self.cid @@ -208,7 +213,12 @@ class CircuitTermination( class Meta: ordering = ['circuit', 'term_side'] - unique_together = ['circuit', 'term_side'] + constraints = ( + models.UniqueConstraint( + fields=('circuit', 'term_side'), + name='%(app_label)s_%(class)s_unique_circuit_term_side' + ), + ) def __str__(self): return f'Termination {self.term_side}: {self.site or self.provider_network}' diff --git a/netbox/circuits/models/providers.py b/netbox/circuits/models/providers.py index e136e13ea..2a1e01626 100644 --- a/netbox/circuits/models/providers.py +++ b/netbox/circuits/models/providers.py @@ -106,10 +106,9 @@ class ProviderNetwork(NetBoxModel): constraints = ( models.UniqueConstraint( fields=('provider', 'name'), - name='circuits_providernetwork_provider_name' + name='%(app_label)s_%(class)s_unique_provider_name' ), ) - unique_together = ('provider', 'name') def __str__(self): return self.name diff --git a/netbox/dcim/migrations/0162_unique_constraints.py b/netbox/dcim/migrations/0162_unique_constraints.py index 08c113f50..a2f471632 100644 --- a/netbox/dcim/migrations/0162_unique_constraints.py +++ b/netbox/dcim/migrations/0162_unique_constraints.py @@ -1,5 +1,3 @@ -# Generated by Django 4.1.1 on 2022-09-14 20:57 - from django.db import migrations, models @@ -34,10 +32,134 @@ class Migration(migrations.Migration): model_name='sitegroup', name='dcim_sitegroup_slug', ), + migrations.AlterUniqueTogether( + name='consoleport', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='consoleporttemplate', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='consoleserverport', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='consoleserverporttemplate', + unique_together=set(), + ), migrations.AlterUniqueTogether( name='device', unique_together=set(), ), + migrations.AlterUniqueTogether( + name='devicebay', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='devicebaytemplate', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='devicetype', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='frontport', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='frontporttemplate', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='interface', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='interfacetemplate', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='inventoryitem', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='inventoryitemtemplate', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='modulebay', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='modulebaytemplate', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='moduletype', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='powerfeed', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='poweroutlet', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='poweroutlettemplate', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='powerpanel', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='powerport', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='powerporttemplate', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='rack', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='rearport', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='rearporttemplate', + unique_together=set(), + ), + migrations.AddConstraint( + model_name='consoleport', + constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_consoleport_unique_device_name'), + ), + migrations.AddConstraint( + model_name='consoleporttemplate', + constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_consoleporttemplate_unique_device_type_name'), + ), + migrations.AddConstraint( + model_name='consoleporttemplate', + constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_consoleporttemplate_unique_module_type_name'), + ), + migrations.AddConstraint( + model_name='consoleserverport', + constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_consoleserverport_unique_device_name'), + ), + migrations.AddConstraint( + model_name='consoleserverporttemplate', + constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_consoleserverporttemplate_unique_device_type_name'), + ), + migrations.AddConstraint( + model_name='consoleserverporttemplate', + constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_consoleserverporttemplate_unique_module_type_name'), + ), migrations.AddConstraint( model_name='device', constraint=models.UniqueConstraint(fields=('name', 'site', 'tenant'), name='dcim_device_unique_name_site_tenant'), @@ -54,6 +176,62 @@ class Migration(migrations.Migration): model_name='device', constraint=models.UniqueConstraint(fields=('virtual_chassis', 'vc_position'), name='dcim_device_unique_virtual_chassis_vc_position'), ), + migrations.AddConstraint( + model_name='devicebay', + constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_devicebay_unique_device_name'), + ), + migrations.AddConstraint( + model_name='devicebaytemplate', + constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_devicebaytemplate_unique_device_type_name'), + ), + migrations.AddConstraint( + model_name='devicetype', + constraint=models.UniqueConstraint(fields=('manufacturer', 'model'), name='dcim_devicetype_unique_manufacturer_model'), + ), + migrations.AddConstraint( + model_name='devicetype', + constraint=models.UniqueConstraint(fields=('manufacturer', 'slug'), name='dcim_devicetype_unique_manufacturer_slug'), + ), + migrations.AddConstraint( + model_name='frontport', + constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_frontport_unique_device_name'), + ), + migrations.AddConstraint( + model_name='frontport', + constraint=models.UniqueConstraint(fields=('rear_port', 'rear_port_position'), name='dcim_frontport_unique_rear_port_position'), + ), + migrations.AddConstraint( + model_name='frontporttemplate', + constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_frontporttemplate_unique_device_type_name'), + ), + migrations.AddConstraint( + model_name='frontporttemplate', + constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_frontporttemplate_unique_module_type_name'), + ), + migrations.AddConstraint( + model_name='frontporttemplate', + constraint=models.UniqueConstraint(fields=('rear_port', 'rear_port_position'), name='dcim_frontporttemplate_unique_rear_port_position'), + ), + migrations.AddConstraint( + model_name='interface', + constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_interface_unique_device_name'), + ), + migrations.AddConstraint( + model_name='interfacetemplate', + constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_interfacetemplate_unique_device_type_name'), + ), + migrations.AddConstraint( + model_name='interfacetemplate', + constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_interfacetemplate_unique_module_type_name'), + ), + migrations.AddConstraint( + model_name='inventoryitem', + constraint=models.UniqueConstraint(fields=('device', 'parent', 'name'), name='dcim_inventoryitem_unique_device_parent_name'), + ), + migrations.AddConstraint( + model_name='inventoryitemtemplate', + constraint=models.UniqueConstraint(fields=('device_type', 'parent', 'name'), name='dcim_inventoryitemtemplate_unique_device_type_parent_name'), + ), migrations.AddConstraint( model_name='location', constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('site', 'name'), name='dcim_location_name', violation_error_message='A location with this name already exists within the specified site.'), @@ -62,6 +240,70 @@ class Migration(migrations.Migration): model_name='location', constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('site', 'slug'), name='dcim_location_slug', violation_error_message='A location with this slug already exists within the specified site.'), ), + migrations.AddConstraint( + model_name='modulebay', + constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_modulebay_unique_device_name'), + ), + migrations.AddConstraint( + model_name='modulebaytemplate', + constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_modulebaytemplate_unique_device_type_name'), + ), + migrations.AddConstraint( + model_name='moduletype', + constraint=models.UniqueConstraint(fields=('manufacturer', 'model'), name='dcim_moduletype_unique_manufacturer_model'), + ), + migrations.AddConstraint( + model_name='powerfeed', + constraint=models.UniqueConstraint(fields=('power_panel', 'name'), name='dcim_powerfeed_unique_power_panel_name'), + ), + migrations.AddConstraint( + model_name='poweroutlet', + constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_poweroutlet_unique_device_name'), + ), + migrations.AddConstraint( + model_name='poweroutlettemplate', + constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_poweroutlettemplate_unique_device_type_name'), + ), + migrations.AddConstraint( + model_name='poweroutlettemplate', + constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_poweroutlettemplate_unique_module_type_name'), + ), + migrations.AddConstraint( + model_name='powerpanel', + constraint=models.UniqueConstraint(fields=('site', 'name'), name='dcim_powerpanel_unique_site_name'), + ), + migrations.AddConstraint( + model_name='powerport', + constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_powerport_unique_device_name'), + ), + migrations.AddConstraint( + model_name='powerporttemplate', + constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_powerporttemplate_unique_device_type_name'), + ), + migrations.AddConstraint( + model_name='powerporttemplate', + constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_powerporttemplate_unique_module_type_name'), + ), + migrations.AddConstraint( + model_name='rack', + constraint=models.UniqueConstraint(fields=('location', 'name'), name='dcim_rack_unique_location_name'), + ), + migrations.AddConstraint( + model_name='rack', + constraint=models.UniqueConstraint(fields=('location', 'facility_id'), name='dcim_rack_unique_location_facility_id'), + ), + migrations.AddConstraint( + model_name='rearport', + constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_rearport_unique_device_name'), + ), + migrations.AddConstraint( + model_name='rearporttemplate', + constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_rearporttemplate_unique_device_type_name'), + ), + migrations.AddConstraint( + model_name='rearporttemplate', + constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_rearporttemplate_unique_module_type_name'), + ), migrations.AddConstraint( model_name='region', constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('name',), name='dcim_region_name', violation_error_message='A top-level region with this name already exists.'), diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index b7079d375..15389a2c0 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -61,6 +61,13 @@ class ComponentTemplateModel(WebhooksMixin, ChangeLoggedModel): class Meta: abstract = True + ordering = ('device_type', '_name') + constraints = ( + models.UniqueConstraint( + fields=('device_type', 'name'), + name='%(app_label)s_%(class)s_unique_device_type_name' + ), + ) def __str__(self): if self.label: @@ -100,6 +107,17 @@ class ModularComponentTemplateModel(ComponentTemplateModel): class Meta: abstract = True + ordering = ('device_type', 'module_type', '_name') + constraints = ( + models.UniqueConstraint( + fields=('device_type', 'name'), + name='%(app_label)s_%(class)s_unique_device_type_name' + ), + models.UniqueConstraint( + fields=('module_type', 'name'), + name='%(app_label)s_%(class)s_unique_module_type_name' + ), + ) def to_objectchange(self, action): objectchange = super().to_objectchange(action) @@ -145,13 +163,6 @@ class ConsolePortTemplate(ModularComponentTemplateModel): component_model = ConsolePort - class Meta: - ordering = ('device_type', 'module_type', '_name') - unique_together = ( - ('device_type', 'name'), - ('module_type', 'name'), - ) - def instantiate(self, **kwargs): return self.component_model( name=self.resolve_name(kwargs.get('module')), @@ -181,13 +192,6 @@ class ConsoleServerPortTemplate(ModularComponentTemplateModel): component_model = ConsoleServerPort - class Meta: - ordering = ('device_type', 'module_type', '_name') - unique_together = ( - ('device_type', 'name'), - ('module_type', 'name'), - ) - def instantiate(self, **kwargs): return self.component_model( name=self.resolve_name(kwargs.get('module')), @@ -229,13 +233,6 @@ class PowerPortTemplate(ModularComponentTemplateModel): component_model = PowerPort - class Meta: - ordering = ('device_type', 'module_type', '_name') - unique_together = ( - ('device_type', 'name'), - ('module_type', 'name'), - ) - def instantiate(self, **kwargs): return self.component_model( name=self.resolve_name(kwargs.get('module')), @@ -291,13 +288,6 @@ class PowerOutletTemplate(ModularComponentTemplateModel): component_model = PowerOutlet - class Meta: - ordering = ('device_type', 'module_type', '_name') - unique_together = ( - ('device_type', 'name'), - ('module_type', 'name'), - ) - def clean(self): super().clean() @@ -372,13 +362,6 @@ class InterfaceTemplate(ModularComponentTemplateModel): component_model = Interface - class Meta: - ordering = ('device_type', 'module_type', '_name') - unique_together = ( - ('device_type', 'name'), - ('module_type', 'name'), - ) - def instantiate(self, **kwargs): return self.component_model( name=self.resolve_name(kwargs.get('module')), @@ -428,12 +411,20 @@ class FrontPortTemplate(ModularComponentTemplateModel): component_model = FrontPort - class Meta: - ordering = ('device_type', 'module_type', '_name') - unique_together = ( - ('device_type', 'name'), - ('module_type', 'name'), - ('rear_port', 'rear_port_position'), + class Meta(ModularComponentTemplateModel.Meta): + constraints = ( + models.UniqueConstraint( + fields=('device_type', 'name'), + name='%(app_label)s_%(class)s_unique_device_type_name' + ), + models.UniqueConstraint( + fields=('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' + ), ) def clean(self): @@ -507,13 +498,6 @@ class RearPortTemplate(ModularComponentTemplateModel): component_model = RearPort - class Meta: - ordering = ('device_type', 'module_type', '_name') - unique_together = ( - ('device_type', 'name'), - ('module_type', 'name'), - ) - def instantiate(self, **kwargs): return self.component_model( name=self.resolve_name(kwargs.get('module')), @@ -547,10 +531,6 @@ class ModuleBayTemplate(ComponentTemplateModel): component_model = ModuleBay - class Meta: - ordering = ('device_type', '_name') - unique_together = ('device_type', 'name') - def instantiate(self, device): return self.component_model( device=device, @@ -574,10 +554,6 @@ class DeviceBayTemplate(ComponentTemplateModel): """ component_model = DeviceBay - class Meta: - ordering = ('device_type', '_name') - unique_together = ('device_type', 'name') - def instantiate(self, device): return self.component_model( device=device, @@ -653,7 +629,12 @@ class InventoryItemTemplate(MPTTModel, ComponentTemplateModel): class Meta: ordering = ('device_type__id', 'parent__id', '_name') - unique_together = ('device_type', 'parent', 'name') + constraints = ( + models.UniqueConstraint( + fields=('device_type', 'parent', 'name'), + name='%(app_label)s_%(class)s_unique_device_type_parent_name' + ), + ) def instantiate(self, **kwargs): parent = InventoryItem.objects.get(name=self.parent.name, **kwargs) if self.parent else None diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index c521ee095..59d63ef7b 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -69,6 +69,13 @@ class ComponentModel(NetBoxModel): class Meta: abstract = True + ordering = ('device', '_name') + constraints = ( + models.UniqueConstraint( + fields=('device', 'name'), + name='%(app_label)s_%(class)s_unique_device_name' + ), + ) def __str__(self): if self.label: @@ -99,7 +106,7 @@ class ModularComponentModel(ComponentModel): object_id_field='component_id' ) - class Meta: + class Meta(ComponentModel.Meta): abstract = True @@ -265,10 +272,6 @@ class ConsolePort(ModularComponentModel, CabledObjectModel, PathEndpoint): clone_fields = ('device', 'module', 'type', 'speed') - class Meta: - ordering = ('device', '_name') - unique_together = ('device', 'name') - def get_absolute_url(self): return reverse('dcim:consoleport', kwargs={'pk': self.pk}) @@ -292,10 +295,6 @@ class ConsoleServerPort(ModularComponentModel, CabledObjectModel, PathEndpoint): clone_fields = ('device', 'module', 'type', 'speed') - class Meta: - ordering = ('device', '_name') - unique_together = ('device', 'name') - def get_absolute_url(self): return reverse('dcim:consoleserverport', kwargs={'pk': self.pk}) @@ -329,10 +328,6 @@ class PowerPort(ModularComponentModel, CabledObjectModel, PathEndpoint): clone_fields = ('device', 'module', 'maximum_draw', 'allocated_draw') - class Meta: - ordering = ('device', '_name') - unique_together = ('device', 'name') - def get_absolute_url(self): return reverse('dcim:powerport', kwargs={'pk': self.pk}) @@ -443,10 +438,6 @@ class PowerOutlet(ModularComponentModel, CabledObjectModel, PathEndpoint): clone_fields = ('device', 'module', 'type', 'power_port', 'feed_leg') - class Meta: - ordering = ('device', '_name') - unique_together = ('device', 'name') - def get_absolute_url(self): return reverse('dcim:poweroutlet', kwargs={'pk': self.pk}) @@ -677,9 +668,8 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'poe_mode', 'poe_type', 'vrf', ) - class Meta: + class Meta(ModularComponentModel.Meta): ordering = ('device', CollateAsChar('_name')) - unique_together = ('device', 'name') def get_absolute_url(self): return reverse('dcim:interface', kwargs={'pk': self.pk}) @@ -895,11 +885,16 @@ class FrontPort(ModularComponentModel, CabledObjectModel): clone_fields = ('device', 'type', 'color') - class Meta: - ordering = ('device', '_name') - unique_together = ( - ('device', 'name'), - ('rear_port', 'rear_port_position'), + class Meta(ModularComponentModel.Meta): + constraints = ( + models.UniqueConstraint( + fields=('device', 'name'), + name='%(app_label)s_%(class)s_unique_device_name' + ), + models.UniqueConstraint( + fields=('rear_port', 'rear_port_position'), + name='%(app_label)s_%(class)s_unique_rear_port_position' + ), ) def get_absolute_url(self): @@ -944,10 +939,6 @@ class RearPort(ModularComponentModel, CabledObjectModel): ) clone_fields = ('device', 'type', 'color', 'positions') - class Meta: - ordering = ('device', '_name') - unique_together = ('device', 'name') - def get_absolute_url(self): return reverse('dcim:rearport', kwargs={'pk': self.pk}) @@ -980,10 +971,6 @@ class ModuleBay(ComponentModel): clone_fields = ('device',) - class Meta: - ordering = ('device', '_name') - unique_together = ('device', 'name') - def get_absolute_url(self): return reverse('dcim:modulebay', kwargs={'pk': self.pk}) @@ -1002,10 +989,6 @@ class DeviceBay(ComponentModel): clone_fields = ('device',) - class Meta: - ordering = ('device', '_name') - unique_together = ('device', 'name') - def get_absolute_url(self): return reverse('dcim:devicebay', kwargs={'pk': self.pk}) @@ -1141,7 +1124,12 @@ class InventoryItem(MPTTModel, ComponentModel): class Meta: ordering = ('device__id', 'parent__id', '_name') - unique_together = ('device', 'parent', 'name') + constraints = ( + models.UniqueConstraint( + fields=('device', 'parent', 'name'), + name='%(app_label)s_%(class)s_unique_device_parent_name' + ), + ) def get_absolute_url(self): return reverse('dcim:inventoryitem', kwargs={'pk': self.pk}) diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index eb21e532b..491846c39 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -143,10 +143,16 @@ class DeviceType(NetBoxModel): class Meta: ordering = ['manufacturer', 'model'] - unique_together = [ - ['manufacturer', 'model'], - ['manufacturer', 'slug'], - ] + constraints = ( + models.UniqueConstraint( + fields=('manufacturer', 'model'), + name='%(app_label)s_%(class)s_unique_manufacturer_model' + ), + models.UniqueConstraint( + fields=('manufacturer', 'slug'), + name='%(app_label)s_%(class)s_unique_manufacturer_slug' + ), + ) def __str__(self): return self.model @@ -341,8 +347,11 @@ class ModuleType(NetBoxModel): class Meta: ordering = ('manufacturer', 'model') - unique_together = ( - ('manufacturer', 'model'), + constraints = ( + models.UniqueConstraint( + fields=('manufacturer', 'model'), + name='%(app_label)s_%(class)s_unique_manufacturer_model' + ), ) def __str__(self): diff --git a/netbox/dcim/models/power.py b/netbox/dcim/models/power.py index 83eead67f..39f0f37ef 100644 --- a/netbox/dcim/models/power.py +++ b/netbox/dcim/models/power.py @@ -50,7 +50,12 @@ class PowerPanel(NetBoxModel): class Meta: ordering = ['site', 'name'] - unique_together = ['site', 'name'] + constraints = ( + models.UniqueConstraint( + fields=('site', 'name'), + name='%(app_label)s_%(class)s_unique_site_name' + ), + ) def __str__(self): return self.name @@ -138,7 +143,12 @@ class PowerFeed(NetBoxModel, PathEndpoint, CabledObjectModel): class Meta: ordering = ['power_panel', 'name'] - unique_together = ['power_panel', 'name'] + constraints = ( + models.UniqueConstraint( + fields=('power_panel', 'name'), + name='%(app_label)s_%(class)s_unique_power_panel_name' + ), + ) def __str__(self): return self.name diff --git a/netbox/dcim/models/racks.py b/netbox/dcim/models/racks.py index 20027675a..10550e906 100644 --- a/netbox/dcim/models/racks.py +++ b/netbox/dcim/models/racks.py @@ -3,12 +3,11 @@ import decimal from django.apps import apps from django.contrib.auth.models import User from django.contrib.contenttypes.fields import GenericRelation -from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.fields import ArrayField from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models -from django.db.models import Count, Sum +from django.db.models import Count from django.urls import reverse from dcim.choices import * @@ -18,7 +17,7 @@ from netbox.models import OrganizationalModel, NetBoxModel from utilities.choices import ColorChoices from utilities.fields import ColorField, NaturalOrderingField from utilities.utils import array_to_string, drange -from .device_components import PowerOutlet, PowerPort +from .device_components import PowerPort from .devices import Device from .power import PowerFeed @@ -191,10 +190,16 @@ class Rack(NetBoxModel): class Meta: ordering = ('site', 'location', '_name', 'pk') # (site, location, name) may be non-unique - unique_together = ( + constraints = ( # Name and facility_id must be unique *only* within a Location - ('location', 'name'), - ('location', 'facility_id'), + models.UniqueConstraint( + fields=('location', 'name'), + name='%(app_label)s_%(class)s_unique_location_name' + ), + models.UniqueConstraint( + fields=('location', 'facility_id'), + name='%(app_label)s_%(class)s_unique_location_facility_id' + ), ) def __str__(self): diff --git a/netbox/extras/migrations/0078_unique_constraints.py b/netbox/extras/migrations/0078_unique_constraints.py new file mode 100644 index 000000000..4a56831a7 --- /dev/null +++ b/netbox/extras/migrations/0078_unique_constraints.py @@ -0,0 +1,27 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0077_customlink_extend_text_and_url'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='exporttemplate', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='webhook', + unique_together=set(), + ), + migrations.AddConstraint( + model_name='exporttemplate', + constraint=models.UniqueConstraint(fields=('content_type', 'name'), name='extras_exporttemplate_unique_content_type_name'), + ), + migrations.AddConstraint( + model_name='webhook', + constraint=models.UniqueConstraint(fields=('payload_url', 'type_create', 'type_update', 'type_delete'), name='extras_webhook_unique_payload_url_types'), + ), + ] diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index 0df34c146..266953f61 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -131,7 +131,12 @@ class Webhook(ExportTemplatesMixin, WebhooksMixin, ChangeLoggedModel): class Meta: ordering = ('name',) - unique_together = ('payload_url', 'type_create', 'type_update', 'type_delete',) + constraints = ( + models.UniqueConstraint( + fields=('payload_url', 'type_create', 'type_update', 'type_delete'), + name='%(app_label)s_%(class)s_unique_payload_url_types' + ), + ) def __str__(self): return self.name @@ -297,9 +302,12 @@ class ExportTemplate(ExportTemplatesMixin, WebhooksMixin, ChangeLoggedModel): class Meta: ordering = ['content_type', 'name'] - unique_together = [ - ['content_type', 'name'] - ] + constraints = ( + models.UniqueConstraint( + fields=('content_type', 'name'), + name='%(app_label)s_%(class)s_unique_content_type_name' + ), + ) def __str__(self): return f"{self.content_type}: {self.name}" diff --git a/netbox/ipam/migrations/0062_unique_constraints.py b/netbox/ipam/migrations/0062_unique_constraints.py new file mode 100644 index 000000000..47c1a1214 --- /dev/null +++ b/netbox/ipam/migrations/0062_unique_constraints.py @@ -0,0 +1,43 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ipam', '0061_fhrpgroup_name'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='fhrpgroupassignment', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='vlan', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='vlangroup', + unique_together=set(), + ), + migrations.AddConstraint( + model_name='fhrpgroupassignment', + constraint=models.UniqueConstraint(fields=('interface_type', 'interface_id', 'group'), name='ipam_fhrpgroupassignment_unique_interface_group'), + ), + migrations.AddConstraint( + model_name='vlan', + constraint=models.UniqueConstraint(fields=('group', 'vid'), name='ipam_vlan_unique_group_vid'), + ), + migrations.AddConstraint( + model_name='vlan', + constraint=models.UniqueConstraint(fields=('group', 'name'), name='ipam_vlan_unique_group_name'), + ), + migrations.AddConstraint( + model_name='vlangroup', + constraint=models.UniqueConstraint(fields=('scope_type', 'scope_id', 'name'), name='ipam_vlangroup_unique_scope_name'), + ), + migrations.AddConstraint( + model_name='vlangroup', + constraint=models.UniqueConstraint(fields=('scope_type', 'scope_id', 'slug'), name='ipam_vlangroup_unique_scope_slug'), + ), + ] diff --git a/netbox/ipam/models/fhrp.py b/netbox/ipam/models/fhrp.py index 88e6e19d9..633affa41 100644 --- a/netbox/ipam/models/fhrp.py +++ b/netbox/ipam/models/fhrp.py @@ -102,7 +102,12 @@ class FHRPGroupAssignment(WebhooksMixin, ChangeLoggedModel): class Meta: ordering = ('-priority', 'pk') - unique_together = ('interface_type', 'interface_id', 'group') + constraints = ( + models.UniqueConstraint( + fields=('interface_type', 'interface_id', 'group'), + name='%(app_label)s_%(class)s_unique_interface_group' + ), + ) verbose_name = 'FHRP group assignment' def __str__(self): diff --git a/netbox/ipam/models/vlans.py b/netbox/ipam/models/vlans.py index f0e062721..c8c401e1c 100644 --- a/netbox/ipam/models/vlans.py +++ b/netbox/ipam/models/vlans.py @@ -70,10 +70,16 @@ class VLANGroup(OrganizationalModel): class Meta: ordering = ('name', 'pk') # Name may be non-unique - unique_together = [ - ['scope_type', 'scope_id', 'name'], - ['scope_type', 'scope_id', 'slug'], - ] + constraints = ( + models.UniqueConstraint( + fields=('scope_type', 'scope_id', 'name'), + name='%(app_label)s_%(class)s_unique_scope_name' + ), + models.UniqueConstraint( + fields=('scope_type', 'scope_id', 'slug'), + name='%(app_label)s_%(class)s_unique_scope_slug' + ), + ) verbose_name = 'VLAN group' verbose_name_plural = 'VLAN groups' @@ -189,10 +195,16 @@ class VLAN(NetBoxModel): class Meta: ordering = ('site', 'group', 'vid', 'pk') # (site, group, vid) may be non-unique - unique_together = [ - ['group', 'vid'], - ['group', 'name'], - ] + constraints = ( + models.UniqueConstraint( + fields=('group', 'vid'), + name='%(app_label)s_%(class)s_unique_group_vid' + ), + models.UniqueConstraint( + fields=('group', 'name'), + name='%(app_label)s_%(class)s_unique_group_name' + ), + ) verbose_name = 'VLAN' verbose_name_plural = 'VLANs' diff --git a/netbox/tenancy/migrations/0008_unique_constraints.py b/netbox/tenancy/migrations/0008_unique_constraints.py new file mode 100644 index 000000000..092878524 --- /dev/null +++ b/netbox/tenancy/migrations/0008_unique_constraints.py @@ -0,0 +1,35 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tenancy', '0007_contact_link'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='contact', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='contactassignment', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='contactgroup', + unique_together=set(), + ), + migrations.AddConstraint( + model_name='contact', + constraint=models.UniqueConstraint(fields=('group', 'name'), name='tenancy_contact_unique_group_name'), + ), + migrations.AddConstraint( + model_name='contactassignment', + constraint=models.UniqueConstraint(fields=('content_type', 'object_id', 'contact', 'role'), name='tenancy_contactassignment_unique_object_contact_role'), + ), + migrations.AddConstraint( + model_name='contactgroup', + constraint=models.UniqueConstraint(fields=('parent', 'name'), name='tenancy_contactgroup_unique_parent_name'), + ), + ] diff --git a/netbox/tenancy/models/contacts.py b/netbox/tenancy/models/contacts.py index 41881f853..79c0a2db3 100644 --- a/netbox/tenancy/models/contacts.py +++ b/netbox/tenancy/models/contacts.py @@ -41,8 +41,11 @@ class ContactGroup(NestedGroupModel): class Meta: ordering = ['name'] - unique_together = ( - ('parent', 'name') + constraints = ( + models.UniqueConstraint( + fields=('parent', 'name'), + name='%(app_label)s_%(class)s_unique_parent_name' + ), ) def get_absolute_url(self): @@ -118,8 +121,11 @@ class Contact(NetBoxModel): class Meta: ordering = ['name'] - unique_together = ( - ('group', 'name') + constraints = ( + models.UniqueConstraint( + fields=('group', 'name'), + name='%(app_label)s_%(class)s_unique_group_name' + ), ) def __str__(self): @@ -159,7 +165,12 @@ class ContactAssignment(WebhooksMixin, ChangeLoggedModel): class Meta: ordering = ('priority', 'contact') - unique_together = ('content_type', 'object_id', 'contact', 'role', 'priority') + constraints = ( + models.UniqueConstraint( + fields=('content_type', 'object_id', 'contact', 'role'), + name='%(app_label)s_%(class)s_unique_object_contact_role' + ), + ) def __str__(self): if self.priority: diff --git a/netbox/virtualization/migrations/0033_unique_constraints.py b/netbox/virtualization/migrations/0033_unique_constraints.py index fe02881b0..4667dcbd3 100644 --- a/netbox/virtualization/migrations/0033_unique_constraints.py +++ b/netbox/virtualization/migrations/0033_unique_constraints.py @@ -1,5 +1,3 @@ -# Generated by Django 4.1.1 on 2022-09-14 20:57 - from django.db import migrations, models @@ -10,10 +8,26 @@ class Migration(migrations.Migration): ] operations = [ + migrations.AlterUniqueTogether( + name='cluster', + unique_together=set(), + ), migrations.AlterUniqueTogether( name='virtualmachine', unique_together=set(), ), + migrations.AlterUniqueTogether( + name='vminterface', + unique_together=set(), + ), + migrations.AddConstraint( + model_name='cluster', + constraint=models.UniqueConstraint(fields=('group', 'name'), name='virtualization_cluster_unique_group_name'), + ), + migrations.AddConstraint( + model_name='cluster', + constraint=models.UniqueConstraint(fields=('site', 'name'), name='virtualization_cluster_unique_site_name'), + ), migrations.AddConstraint( model_name='virtualmachine', constraint=models.UniqueConstraint(fields=('name', 'cluster', 'tenant'), name='virtualization_virtualmachine_unique_name_cluster_tenant'), @@ -22,4 +36,8 @@ class Migration(migrations.Migration): model_name='virtualmachine', constraint=models.UniqueConstraint(condition=models.Q(('tenant__isnull', True)), fields=('name', 'cluster'), name='virtualization_virtualmachine_unique_name_cluster', violation_error_message='Virtual machine name must be unique per site.'), ), + migrations.AddConstraint( + model_name='vminterface', + constraint=models.UniqueConstraint(fields=('virtual_machine', 'name'), name='virtualization_vminterface_unique_virtual_machine_name'), + ), ] diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py index 1b0a6ba06..b0e732188 100644 --- a/netbox/virtualization/models.py +++ b/netbox/virtualization/models.py @@ -160,9 +160,15 @@ class Cluster(NetBoxModel): class Meta: ordering = ['name'] - unique_together = ( - ('group', 'name'), - ('site', 'name'), + constraints = ( + models.UniqueConstraint( + fields=('group', 'name'), + name='%(app_label)s_%(class)s_unique_group_name' + ), + models.UniqueConstraint( + fields=('site', 'name'), + name='%(app_label)s_%(class)s_unique_site_name' + ), ) def __str__(self): @@ -461,9 +467,14 @@ class VMInterface(NetBoxModel, BaseInterface): ) class Meta: - verbose_name = 'interface' ordering = ('virtual_machine', CollateAsChar('_name')) - unique_together = ('virtual_machine', 'name') + constraints = ( + models.UniqueConstraint( + fields=('virtual_machine', 'name'), + name='%(app_label)s_%(class)s_unique_virtual_machine_name' + ), + ) + verbose_name = 'interface' def __str__(self): return self.name diff --git a/netbox/wireless/migrations/0006_unique_constraints.py b/netbox/wireless/migrations/0006_unique_constraints.py new file mode 100644 index 000000000..f638ae1ab --- /dev/null +++ b/netbox/wireless/migrations/0006_unique_constraints.py @@ -0,0 +1,27 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wireless', '0005_wirelesslink_interface_types'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='wirelesslangroup', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='wirelesslink', + unique_together=set(), + ), + migrations.AddConstraint( + model_name='wirelesslangroup', + constraint=models.UniqueConstraint(fields=('parent', 'name'), name='wireless_wirelesslangroup_unique_parent_name'), + ), + migrations.AddConstraint( + model_name='wirelesslink', + constraint=models.UniqueConstraint(fields=('interface_a', 'interface_b'), name='wireless_wirelesslink_unique_interfaces'), + ), + ] diff --git a/netbox/wireless/models.py b/netbox/wireless/models.py index c383ad642..29fe33f4b 100644 --- a/netbox/wireless/models.py +++ b/netbox/wireless/models.py @@ -69,8 +69,11 @@ class WirelessLANGroup(NestedGroupModel): class Meta: ordering = ('name', 'pk') - unique_together = ( - ('parent', 'name') + constraints = ( + models.UniqueConstraint( + fields=('parent', 'name'), + name='%(app_label)s_%(class)s_unique_parent_name' + ), ) verbose_name = 'Wireless LAN Group' @@ -195,7 +198,12 @@ class WirelessLink(WirelessAuthenticationBase, NetBoxModel): class Meta: ordering = ['pk'] - unique_together = ('interface_a', 'interface_b') + constraints = ( + models.UniqueConstraint( + fields=('interface_a', 'interface_b'), + name='%(app_label)s_%(class)s_unique_interfaces' + ), + ) def __str__(self): return f'#{self.pk}'