From 5dd5d65d744801e413c0aef833b72f8d8d49ad82 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 16 Jan 2026 17:33:03 -0500 Subject: [PATCH] Initial testing for #21203 --- netbox/dcim/apps.py | 9 ++++ netbox/dcim/filtersets.py | 7 +-- netbox/dcim/graphql/types.py | 2 +- .../0226_denormalize_search_attrs.py | 46 +++++++++++++++++++ netbox/dcim/models/devices.py | 14 ++++++ 5 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 netbox/dcim/migrations/0226_denormalize_search_attrs.py diff --git a/netbox/dcim/apps.py b/netbox/dcim/apps.py index 67ff17489..67a76769b 100644 --- a/netbox/dcim/apps.py +++ b/netbox/dcim/apps.py @@ -29,6 +29,15 @@ class DCIMConfig(AppConfig): denormalized.register(CableTermination, '_location', { '_site': 'site', }) + denormalized.register(Device, 'virtual_chassis', { + '_virtual_chassis_name': 'name', + }) + denormalized.register(Device, 'primary_ip4', { + '_primary_ip4_address': 'address', + }) + denormalized.register(Device, 'primary_ip6', { + '_primary_ip6_address': 'address', + }) # Register counters connect_counters(Device, DeviceType, ModuleType, RackType, VirtualChassis) diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index e11d32d95..42879a5e1 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -1331,13 +1331,14 @@ class DeviceFilterSet( return queryset return queryset.filter( Q(name__icontains=value) | - Q(virtual_chassis__name__icontains=value) | Q(serial__icontains=value.strip()) | Q(asset_tag__icontains=value.strip()) | Q(description__icontains=value.strip()) | Q(comments__icontains=value) | - Q(primary_ip4__address__startswith=value) | - Q(primary_ip6__address__startswith=value) + # Denormalized fields + Q(_virtual_chassis_name__icontains=value) | + Q(_primary_ip4_address__startswith=value) | + Q(_primary_ip6_address__startswith=value) ).distinct() def _has_primary_ip(self, queryset, name, value): diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py index 1132a0ca9..b582255da 100644 --- a/netbox/dcim/graphql/types.py +++ b/netbox/dcim/graphql/types.py @@ -224,7 +224,7 @@ class ConsoleServerPortTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.Device, - fields='__all__', + exclude=['_virtual_chassis_name', '_primary_ip4_address', '_primary_ip6_address'], filters=DeviceFilter, pagination=True ) diff --git a/netbox/dcim/migrations/0226_denormalize_search_attrs.py b/netbox/dcim/migrations/0226_denormalize_search_attrs.py new file mode 100644 index 000000000..393056f6c --- /dev/null +++ b/netbox/dcim/migrations/0226_denormalize_search_attrs.py @@ -0,0 +1,46 @@ +from django.db import migrations, models +from django.db.models import Q + +import ipam.fields + + +def backfill_denormalized_fields(apps, schema_editor): + Device = apps.get_model('dcim', 'Device') + + # TODO: Optimize for bulk operations + for device in Device.objects.filter( + Q(virtual_chassis__isnull=False) | Q(primary_ip4__isnull=False) | Q(primary_ip6__isnull=False) + ): + device._virtual_chassis_name = device.virtual_chassis.name if device.virtual_chassis else '' + device._primary_ip4_address = device.primary_ip4.address if device.primary_ip4 else None + device._primary_ip6_address = device.primary_ip6.address if device.primary_ip6 else None + device.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0225_gfk_indexes'), + ] + + operations = [ + migrations.AddField( + model_name='device', + name='_primary_ip4_address', + field=ipam.fields.IPAddressField(blank=True, null=True), + ), + migrations.AddField( + model_name='device', + name='_primary_ip6_address', + field=ipam.fields.IPAddressField(blank=True, null=True), + ), + migrations.AddField( + model_name='device', + name='_virtual_chassis_name', + field=models.CharField(blank=True), + ), + migrations.RunPython( + code=backfill_denormalized_fields, + reverse_code=migrations.RunPython.noop + ), + ] diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index 65a5282b0..0f52855d6 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -21,6 +21,7 @@ from dcim.fields import MACAddressField from dcim.utils import create_port_mappings, update_interface_bridges from extras.models import ConfigContextModel, CustomField from extras.querysets import ConfigContextModelQuerySet +from ipam.fields import IPAddressField from netbox.choices import ColorChoices from netbox.config import ConfigItem from netbox.models import NestedGroupModel, OrganizationalModel, PrimaryModel @@ -673,6 +674,19 @@ class Device( related_query_name='device', ) + # Denormalized fields + _virtual_chassis_name = models.CharField( + blank=True, + ) + _primary_ip4_address = IPAddressField( + blank=True, + null=True, + ) + _primary_ip6_address = IPAddressField( + blank=True, + null=True, + ) + # Counter fields console_port_count = CounterCacheField( to_model='dcim.ConsolePort',