2
0
Эх сурвалжийг харах

Replace unique_together with UniqueConstraints

jeremystretch 3 жил өмнө
parent
commit
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:
     class Meta:
         ordering = ['provider', 'cid']
         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):
     def __str__(self):
         return self.cid
         return self.cid
@@ -208,7 +213,12 @@ class CircuitTermination(
 
 
     class Meta:
     class Meta:
         ordering = ['circuit', 'term_side']
         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):
     def __str__(self):
         return f'Termination {self.term_side}: {self.site or self.provider_network}'
         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 = (
         constraints = (
             models.UniqueConstraint(
             models.UniqueConstraint(
                 fields=('provider', 'name'),
                 fields=('provider', 'name'),
-                name='circuits_providernetwork_provider_name'
+                name='%(app_label)s_%(class)s_unique_provider_name'
             ),
             ),
         )
         )
-        unique_together = ('provider', 'name')
 
 
     def __str__(self):
     def __str__(self):
         return self.name
         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
 from django.db import migrations, models
 
 
 
 
@@ -34,10 +32,134 @@ class Migration(migrations.Migration):
             model_name='sitegroup',
             model_name='sitegroup',
             name='dcim_sitegroup_slug',
             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(
         migrations.AlterUniqueTogether(
             name='device',
             name='device',
             unique_together=set(),
             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(
         migrations.AddConstraint(
             model_name='device',
             model_name='device',
             constraint=models.UniqueConstraint(fields=('name', 'site', 'tenant'), name='dcim_device_unique_name_site_tenant'),
             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',
             model_name='device',
             constraint=models.UniqueConstraint(fields=('virtual_chassis', 'vc_position'), name='dcim_device_unique_virtual_chassis_vc_position'),
             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(
         migrations.AddConstraint(
             model_name='location',
             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.'),
             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',
             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.'),
             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(
         migrations.AddConstraint(
             model_name='region',
             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.'),
             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:
     class Meta:
         abstract = True
         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):
     def __str__(self):
         if self.label:
         if self.label:
@@ -100,6 +107,17 @@ class ModularComponentTemplateModel(ComponentTemplateModel):
 
 
     class Meta:
     class Meta:
         abstract = True
         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):
     def to_objectchange(self, action):
         objectchange = super().to_objectchange(action)
         objectchange = super().to_objectchange(action)
@@ -145,13 +163,6 @@ class ConsolePortTemplate(ModularComponentTemplateModel):
 
 
     component_model = ConsolePort
     component_model = ConsolePort
 
 
-    class Meta:
-        ordering = ('device_type', 'module_type', '_name')
-        unique_together = (
-            ('device_type', 'name'),
-            ('module_type', 'name'),
-        )
-
     def instantiate(self, **kwargs):
     def instantiate(self, **kwargs):
         return self.component_model(
         return self.component_model(
             name=self.resolve_name(kwargs.get('module')),
             name=self.resolve_name(kwargs.get('module')),
@@ -181,13 +192,6 @@ class ConsoleServerPortTemplate(ModularComponentTemplateModel):
 
 
     component_model = ConsoleServerPort
     component_model = ConsoleServerPort
 
 
-    class Meta:
-        ordering = ('device_type', 'module_type', '_name')
-        unique_together = (
-            ('device_type', 'name'),
-            ('module_type', 'name'),
-        )
-
     def instantiate(self, **kwargs):
     def instantiate(self, **kwargs):
         return self.component_model(
         return self.component_model(
             name=self.resolve_name(kwargs.get('module')),
             name=self.resolve_name(kwargs.get('module')),
@@ -229,13 +233,6 @@ class PowerPortTemplate(ModularComponentTemplateModel):
 
 
     component_model = PowerPort
     component_model = PowerPort
 
 
-    class Meta:
-        ordering = ('device_type', 'module_type', '_name')
-        unique_together = (
-            ('device_type', 'name'),
-            ('module_type', 'name'),
-        )
-
     def instantiate(self, **kwargs):
     def instantiate(self, **kwargs):
         return self.component_model(
         return self.component_model(
             name=self.resolve_name(kwargs.get('module')),
             name=self.resolve_name(kwargs.get('module')),
@@ -291,13 +288,6 @@ class PowerOutletTemplate(ModularComponentTemplateModel):
 
 
     component_model = PowerOutlet
     component_model = PowerOutlet
 
 
-    class Meta:
-        ordering = ('device_type', 'module_type', '_name')
-        unique_together = (
-            ('device_type', 'name'),
-            ('module_type', 'name'),
-        )
-
     def clean(self):
     def clean(self):
         super().clean()
         super().clean()
 
 
@@ -372,13 +362,6 @@ class InterfaceTemplate(ModularComponentTemplateModel):
 
 
     component_model = Interface
     component_model = Interface
 
 
-    class Meta:
-        ordering = ('device_type', 'module_type', '_name')
-        unique_together = (
-            ('device_type', 'name'),
-            ('module_type', 'name'),
-        )
-
     def instantiate(self, **kwargs):
     def instantiate(self, **kwargs):
         return self.component_model(
         return self.component_model(
             name=self.resolve_name(kwargs.get('module')),
             name=self.resolve_name(kwargs.get('module')),
@@ -428,12 +411,20 @@ class FrontPortTemplate(ModularComponentTemplateModel):
 
 
     component_model = FrontPort
     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):
     def clean(self):
@@ -507,13 +498,6 @@ class RearPortTemplate(ModularComponentTemplateModel):
 
 
     component_model = RearPort
     component_model = RearPort
 
 
-    class Meta:
-        ordering = ('device_type', 'module_type', '_name')
-        unique_together = (
-            ('device_type', 'name'),
-            ('module_type', 'name'),
-        )
-
     def instantiate(self, **kwargs):
     def instantiate(self, **kwargs):
         return self.component_model(
         return self.component_model(
             name=self.resolve_name(kwargs.get('module')),
             name=self.resolve_name(kwargs.get('module')),
@@ -547,10 +531,6 @@ class ModuleBayTemplate(ComponentTemplateModel):
 
 
     component_model = ModuleBay
     component_model = ModuleBay
 
 
-    class Meta:
-        ordering = ('device_type', '_name')
-        unique_together = ('device_type', 'name')
-
     def instantiate(self, device):
     def instantiate(self, device):
         return self.component_model(
         return self.component_model(
             device=device,
             device=device,
@@ -574,10 +554,6 @@ class DeviceBayTemplate(ComponentTemplateModel):
     """
     """
     component_model = DeviceBay
     component_model = DeviceBay
 
 
-    class Meta:
-        ordering = ('device_type', '_name')
-        unique_together = ('device_type', 'name')
-
     def instantiate(self, device):
     def instantiate(self, device):
         return self.component_model(
         return self.component_model(
             device=device,
             device=device,
@@ -653,7 +629,12 @@ class InventoryItemTemplate(MPTTModel, ComponentTemplateModel):
 
 
     class Meta:
     class Meta:
         ordering = ('device_type__id', 'parent__id', '_name')
         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):
     def instantiate(self, **kwargs):
         parent = InventoryItem.objects.get(name=self.parent.name, **kwargs) if self.parent else None
         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:
     class Meta:
         abstract = True
         abstract = True
+        ordering = ('device', '_name')
+        constraints = (
+            models.UniqueConstraint(
+                fields=('device', 'name'),
+                name='%(app_label)s_%(class)s_unique_device_name'
+            ),
+        )
 
 
     def __str__(self):
     def __str__(self):
         if self.label:
         if self.label:
@@ -99,7 +106,7 @@ class ModularComponentModel(ComponentModel):
         object_id_field='component_id'
         object_id_field='component_id'
     )
     )
 
 
-    class Meta:
+    class Meta(ComponentModel.Meta):
         abstract = True
         abstract = True
 
 
 
 
@@ -265,10 +272,6 @@ class ConsolePort(ModularComponentModel, CabledObjectModel, PathEndpoint):
 
 
     clone_fields = ('device', 'module', 'type', 'speed')
     clone_fields = ('device', 'module', 'type', 'speed')
 
 
-    class Meta:
-        ordering = ('device', '_name')
-        unique_together = ('device', 'name')
-
     def get_absolute_url(self):
     def get_absolute_url(self):
         return reverse('dcim:consoleport', kwargs={'pk': self.pk})
         return reverse('dcim:consoleport', kwargs={'pk': self.pk})
 
 
@@ -292,10 +295,6 @@ class ConsoleServerPort(ModularComponentModel, CabledObjectModel, PathEndpoint):
 
 
     clone_fields = ('device', 'module', 'type', 'speed')
     clone_fields = ('device', 'module', 'type', 'speed')
 
 
-    class Meta:
-        ordering = ('device', '_name')
-        unique_together = ('device', 'name')
-
     def get_absolute_url(self):
     def get_absolute_url(self):
         return reverse('dcim:consoleserverport', kwargs={'pk': self.pk})
         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')
     clone_fields = ('device', 'module', 'maximum_draw', 'allocated_draw')
 
 
-    class Meta:
-        ordering = ('device', '_name')
-        unique_together = ('device', 'name')
-
     def get_absolute_url(self):
     def get_absolute_url(self):
         return reverse('dcim:powerport', kwargs={'pk': self.pk})
         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')
     clone_fields = ('device', 'module', 'type', 'power_port', 'feed_leg')
 
 
-    class Meta:
-        ordering = ('device', '_name')
-        unique_together = ('device', 'name')
-
     def get_absolute_url(self):
     def get_absolute_url(self):
         return reverse('dcim:poweroutlet', kwargs={'pk': self.pk})
         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',
         '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'))
         ordering = ('device', CollateAsChar('_name'))
-        unique_together = ('device', 'name')
 
 
     def get_absolute_url(self):
     def get_absolute_url(self):
         return reverse('dcim:interface', kwargs={'pk': self.pk})
         return reverse('dcim:interface', kwargs={'pk': self.pk})
@@ -895,11 +885,16 @@ class FrontPort(ModularComponentModel, CabledObjectModel):
 
 
     clone_fields = ('device', 'type', 'color')
     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):
     def get_absolute_url(self):
@@ -944,10 +939,6 @@ class RearPort(ModularComponentModel, CabledObjectModel):
     )
     )
     clone_fields = ('device', 'type', 'color', 'positions')
     clone_fields = ('device', 'type', 'color', 'positions')
 
 
-    class Meta:
-        ordering = ('device', '_name')
-        unique_together = ('device', 'name')
-
     def get_absolute_url(self):
     def get_absolute_url(self):
         return reverse('dcim:rearport', kwargs={'pk': self.pk})
         return reverse('dcim:rearport', kwargs={'pk': self.pk})
 
 
@@ -980,10 +971,6 @@ class ModuleBay(ComponentModel):
 
 
     clone_fields = ('device',)
     clone_fields = ('device',)
 
 
-    class Meta:
-        ordering = ('device', '_name')
-        unique_together = ('device', 'name')
-
     def get_absolute_url(self):
     def get_absolute_url(self):
         return reverse('dcim:modulebay', kwargs={'pk': self.pk})
         return reverse('dcim:modulebay', kwargs={'pk': self.pk})
 
 
@@ -1002,10 +989,6 @@ class DeviceBay(ComponentModel):
 
 
     clone_fields = ('device',)
     clone_fields = ('device',)
 
 
-    class Meta:
-        ordering = ('device', '_name')
-        unique_together = ('device', 'name')
-
     def get_absolute_url(self):
     def get_absolute_url(self):
         return reverse('dcim:devicebay', kwargs={'pk': self.pk})
         return reverse('dcim:devicebay', kwargs={'pk': self.pk})
 
 
@@ -1141,7 +1124,12 @@ class InventoryItem(MPTTModel, ComponentModel):
 
 
     class Meta:
     class Meta:
         ordering = ('device__id', 'parent__id', '_name')
         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):
     def get_absolute_url(self):
         return reverse('dcim:inventoryitem', kwargs={'pk': self.pk})
         return reverse('dcim:inventoryitem', kwargs={'pk': self.pk})

+ 15 - 6
netbox/dcim/models/devices.py

@@ -143,10 +143,16 @@ class DeviceType(NetBoxModel):
 
 
     class Meta:
     class Meta:
         ordering = ['manufacturer', 'model']
         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):
     def __str__(self):
         return self.model
         return self.model
@@ -341,8 +347,11 @@ class ModuleType(NetBoxModel):
 
 
     class Meta:
     class Meta:
         ordering = ('manufacturer', 'model')
         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):
     def __str__(self):

+ 12 - 2
netbox/dcim/models/power.py

@@ -50,7 +50,12 @@ class PowerPanel(NetBoxModel):
 
 
     class Meta:
     class Meta:
         ordering = ['site', 'name']
         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):
     def __str__(self):
         return self.name
         return self.name
@@ -138,7 +143,12 @@ class PowerFeed(NetBoxModel, PathEndpoint, CabledObjectModel):
 
 
     class Meta:
     class Meta:
         ordering = ['power_panel', 'name']
         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):
     def __str__(self):
         return self.name
         return self.name

+ 11 - 6
netbox/dcim/models/racks.py

@@ -3,12 +3,11 @@ import decimal
 from django.apps import apps
 from django.apps import apps
 from django.contrib.auth.models import User
 from django.contrib.auth.models import User
 from django.contrib.contenttypes.fields import GenericRelation
 from django.contrib.contenttypes.fields import GenericRelation
-from django.contrib.contenttypes.models import ContentType
 from django.contrib.postgres.fields import ArrayField
 from django.contrib.postgres.fields import ArrayField
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.db import models
 from django.db import models
-from django.db.models import Count, Sum
+from django.db.models import Count
 from django.urls import reverse
 from django.urls import reverse
 
 
 from dcim.choices import *
 from dcim.choices import *
@@ -18,7 +17,7 @@ from netbox.models import OrganizationalModel, NetBoxModel
 from utilities.choices import ColorChoices
 from utilities.choices import ColorChoices
 from utilities.fields import ColorField, NaturalOrderingField
 from utilities.fields import ColorField, NaturalOrderingField
 from utilities.utils import array_to_string, drange
 from utilities.utils import array_to_string, drange
-from .device_components import PowerOutlet, PowerPort
+from .device_components import PowerPort
 from .devices import Device
 from .devices import Device
 from .power import PowerFeed
 from .power import PowerFeed
 
 
@@ -191,10 +190,16 @@ class Rack(NetBoxModel):
 
 
     class Meta:
     class Meta:
         ordering = ('site', 'location', '_name', 'pk')  # (site, location, name) may be non-unique
         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
             # 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):
     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:
     class Meta:
         ordering = ('name',)
         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):
     def __str__(self):
         return self.name
         return self.name
@@ -297,9 +302,12 @@ class ExportTemplate(ExportTemplatesMixin, WebhooksMixin, ChangeLoggedModel):
 
 
     class Meta:
     class Meta:
         ordering = ['content_type', 'name']
         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):
     def __str__(self):
         return f"{self.content_type}: {self.name}"
         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:
     class Meta:
         ordering = ('-priority', 'pk')
         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'
         verbose_name = 'FHRP group assignment'
 
 
     def __str__(self):
     def __str__(self):

+ 20 - 8
netbox/ipam/models/vlans.py

@@ -70,10 +70,16 @@ class VLANGroup(OrganizationalModel):
 
 
     class Meta:
     class Meta:
         ordering = ('name', 'pk')  # Name may be non-unique
         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 = 'VLAN group'
         verbose_name_plural = 'VLAN groups'
         verbose_name_plural = 'VLAN groups'
 
 
@@ -189,10 +195,16 @@ class VLAN(NetBoxModel):
 
 
     class Meta:
     class Meta:
         ordering = ('site', 'group', 'vid', 'pk')  # (site, group, vid) may be non-unique
         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 = 'VLAN'
         verbose_name_plural = 'VLANs'
         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:
     class Meta:
         ordering = ['name']
         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):
     def get_absolute_url(self):
@@ -118,8 +121,11 @@ class Contact(NetBoxModel):
 
 
     class Meta:
     class Meta:
         ordering = ['name']
         ordering = ['name']
-        unique_together = (
-            ('group', 'name')
+        constraints = (
+            models.UniqueConstraint(
+                fields=('group', 'name'),
+                name='%(app_label)s_%(class)s_unique_group_name'
+            ),
         )
         )
 
 
     def __str__(self):
     def __str__(self):
@@ -159,7 +165,12 @@ class ContactAssignment(WebhooksMixin, ChangeLoggedModel):
 
 
     class Meta:
     class Meta:
         ordering = ('priority', 'contact')
         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):
     def __str__(self):
         if self.priority:
         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
 from django.db import migrations, models
 
 
 
 
@@ -10,10 +8,26 @@ class Migration(migrations.Migration):
     ]
     ]
 
 
     operations = [
     operations = [
+        migrations.AlterUniqueTogether(
+            name='cluster',
+            unique_together=set(),
+        ),
         migrations.AlterUniqueTogether(
         migrations.AlterUniqueTogether(
             name='virtualmachine',
             name='virtualmachine',
             unique_together=set(),
             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(
         migrations.AddConstraint(
             model_name='virtualmachine',
             model_name='virtualmachine',
             constraint=models.UniqueConstraint(fields=('name', 'cluster', 'tenant'), name='virtualization_virtualmachine_unique_name_cluster_tenant'),
             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',
             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.'),
             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:
     class Meta:
         ordering = ['name']
         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):
     def __str__(self):
@@ -461,9 +467,14 @@ class VMInterface(NetBoxModel, BaseInterface):
     )
     )
 
 
     class Meta:
     class Meta:
-        verbose_name = 'interface'
         ordering = ('virtual_machine', CollateAsChar('_name'))
         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):
     def __str__(self):
         return self.name
         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:
     class Meta:
         ordering = ('name', 'pk')
         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'
         verbose_name = 'Wireless LAN Group'
 
 
@@ -195,7 +198,12 @@ class WirelessLink(WirelessAuthenticationBase, NetBoxModel):
 
 
     class Meta:
     class Meta:
         ordering = ['pk']
         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):
     def __str__(self):
         return f'#{self.pk}'
         return f'#{self.pk}'