Просмотр исходного кода

Replace unique_together with UniqueConstraints

jeremystretch 3 лет назад
Родитель
Сommit
f51415cf2c

+ 39 - 0
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'),
+        ),
+    ]

+ 12 - 2
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}'

+ 1 - 2
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

+ 244 - 2
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.'),

+ 38 - 57
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

+ 25 - 37
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})

+ 15 - 6
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):

+ 12 - 2
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

+ 11 - 6
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):

+ 27 - 0
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'),
+        ),
+    ]

+ 12 - 4
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}"

+ 43 - 0
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'),
+        ),
+    ]

+ 6 - 1
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):

+ 20 - 8
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'
 

+ 35 - 0
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'),
+        ),
+    ]

+ 16 - 5
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:

+ 20 - 2
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'),
+        ),
     ]

+ 16 - 5
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

+ 27 - 0
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'),
+        ),
+    ]

+ 11 - 3
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}'