1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

#2269: Allow non-unique Device names

This commit is contained in:
Jeremy Stretch
2019-12-09 11:41:03 -05:00
parent 6fe1b9b37c
commit a8dd060e32
3 changed files with 74 additions and 2 deletions

View File

@ -0,0 +1,23 @@
# Generated by Django 2.2.6 on 2019-12-09 15:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tenancy', '0006_custom_tag_models'),
('dcim', '0085_3569_poweroutlet_fields'),
]
operations = [
migrations.AlterField(
model_name='device',
name='name',
field=models.CharField(blank=True, max_length=64, null=True),
),
migrations.AlterUniqueTogether(
name='device',
unique_together={('rack', 'position', 'face'), ('virtual_chassis', 'vc_position'), ('site', 'tenant', 'name')},
),
]

View File

@ -1523,8 +1523,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
name = models.CharField( name = models.CharField(
max_length=64, max_length=64,
blank=True, blank=True,
null=True, null=True
unique=True
) )
serial = models.CharField( serial = models.CharField(
max_length=50, max_length=50,
@ -1645,6 +1644,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
unique_together = [ unique_together = [
['site', 'tenant', 'name'], # See validate_unique below
['rack', 'position', 'face'], ['rack', 'position', 'face'],
['virtual_chassis', 'vc_position'], ['virtual_chassis', 'vc_position'],
] ]
@ -1659,6 +1659,18 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('dcim:device', args=[self.pk]) return reverse('dcim:device', args=[self.pk])
def validate_unique(self, exclude=None):
# Check for a duplicate name on a device assigned to the same Site and no Tenant. This is necessary
# because Django does not consider two NULL fields to be equal, and thus will not trigger a violation
# of the uniqueness constraint without manual intervention.
if self.tenant is None and Device.objects.exclude(pk=self.pk).filter(name=self.name, tenant__isnull=True):
raise ValidationError({
'name': 'A device with this name already exists.'
})
super().validate_unique(exclude)
def clean(self): def clean(self):
super().clean() super().clean()

View File

@ -1,6 +1,7 @@
from django.test import TestCase from django.test import TestCase
from dcim.models import * from dcim.models import *
from tenancy.models import Tenant
class RackTestCase(TestCase): class RackTestCase(TestCase):
@ -281,6 +282,42 @@ class DeviceTestCase(TestCase):
name='Device Bay 1' name='Device Bay 1'
) )
def test_device_duplicate_name_per_site(self):
device1 = Device(
site=self.site,
device_type=self.device_type,
device_role=self.device_role,
name='Test Device 1'
)
device1.save()
device2 = Device(
site=device1.site,
device_type=device1.device_type,
device_role=device1.device_role,
name=device1.name
)
# Two devices assigned to the same Site and no Tenant should fail validation
with self.assertRaises(ValidationError):
device2.full_clean()
tenant = Tenant.objects.create(name='Test Tenant 1', slug='test-tenant-1')
device1.tenant = tenant
device1.save()
device2.tenant = tenant
# Two devices assigned to the same Site and the same Tenant should fail validation
with self.assertRaises(ValidationError):
device2.full_clean()
device2.tenant = None
# Two devices assigned to the same Site and different Tenants should pass validation
device2.full_clean()
device2.save()
class CableTestCase(TestCase): class CableTestCase(TestCase):