From a1cd81ff35ccc3576d6f96c3154a2ef1a7f2a61f Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 7 Jul 2025 13:38:01 -0400 Subject: [PATCH] Closes #17413: Permit identical names for platforms belonging to different manufacturers (#19814) --- docs/models/dcim/platform.md | 4 +- .../0208_platform_manufacturer_uniqueness.py | 54 +++++++++++++++++++ netbox/dcim/models/devices.py | 31 +++++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 netbox/dcim/migrations/0208_platform_manufacturer_uniqueness.py diff --git a/docs/models/dcim/platform.md b/docs/models/dcim/platform.md index 0914d0aa6..35b0b68eb 100644 --- a/docs/models/dcim/platform.md +++ b/docs/models/dcim/platform.md @@ -10,11 +10,11 @@ The assignment of platforms to devices is an optional feature, and may be disreg ### Name -A unique human-friendly name. +A human-friendly name for the platform. Must be unique per manufacturer. ### Slug -A unique URL-friendly identifier. (This value can be used for filtering.) +A URL-friendly identifier; must be unique per manufacturer. (This value can be used for filtering.) ### Manufacturer diff --git a/netbox/dcim/migrations/0208_platform_manufacturer_uniqueness.py b/netbox/dcim/migrations/0208_platform_manufacturer_uniqueness.py new file mode 100644 index 000000000..9659aadf4 --- /dev/null +++ b/netbox/dcim/migrations/0208_platform_manufacturer_uniqueness.py @@ -0,0 +1,54 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0207_remove_redundant_indexes'), + ('extras', '0129_fix_script_paths'), + ] + + operations = [ + migrations.AlterField( + model_name='platform', + name='name', + field=models.CharField(max_length=100), + ), + migrations.AlterField( + model_name='platform', + name='slug', + field=models.SlugField(max_length=100), + ), + migrations.AddConstraint( + model_name='platform', + constraint=models.UniqueConstraint( + fields=('manufacturer', 'name'), + name='dcim_platform_manufacturer_name' + ), + ), + migrations.AddConstraint( + model_name='platform', + constraint=models.UniqueConstraint( + condition=models.Q(('manufacturer__isnull', True)), + fields=('name',), + name='dcim_platform_name', + violation_error_message='Platform name must be unique.' + ), + ), + migrations.AddConstraint( + model_name='platform', + constraint=models.UniqueConstraint( + fields=('manufacturer', 'slug'), + name='dcim_platform_manufacturer_slug' + ), + ), + migrations.AddConstraint( + model_name='platform', + constraint=models.UniqueConstraint( + condition=models.Q(('manufacturer__isnull', True)), + fields=('slug',), + name='dcim_platform_slug', + violation_error_message='Platform slug must be unique.' + ), + ), + ] diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index 5988f8241..f85b4440d 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -415,6 +415,15 @@ class Platform(OrganizationalModel): null=True, help_text=_('Optionally limit this platform to devices of a certain manufacturer') ) + # Override name & slug from OrganizationalModel to not enforce uniqueness + name = models.CharField( + verbose_name=_('name'), + max_length=100 + ) + slug = models.SlugField( + verbose_name=_('slug'), + max_length=100 + ) config_template = models.ForeignKey( to='extras.ConfigTemplate', on_delete=models.PROTECT, @@ -427,6 +436,28 @@ class Platform(OrganizationalModel): ordering = ('name',) verbose_name = _('platform') verbose_name_plural = _('platforms') + constraints = ( + models.UniqueConstraint( + fields=('manufacturer', 'name'), + name='%(app_label)s_%(class)s_manufacturer_name', + ), + models.UniqueConstraint( + fields=('name',), + name='%(app_label)s_%(class)s_name', + condition=Q(manufacturer__isnull=True), + violation_error_message=_("Platform name must be unique.") + ), + models.UniqueConstraint( + fields=('manufacturer', 'slug'), + name='%(app_label)s_%(class)s_manufacturer_slug', + ), + models.UniqueConstraint( + fields=('slug',), + name='%(app_label)s_%(class)s_slug', + condition=Q(manufacturer__isnull=True), + violation_error_message=_("Platform slug must be unique.") + ), + ) class Device(