mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-31 21:06:25 -06:00
Add denormalized relations
This commit is contained in:
parent
f28f1646b0
commit
f34818df71
@ -1,5 +1,7 @@
|
|||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
from netbox import denormalized
|
||||||
|
|
||||||
|
|
||||||
class IPAMConfig(AppConfig):
|
class IPAMConfig(AppConfig):
|
||||||
name = "ipam"
|
name = "ipam"
|
||||||
@ -8,6 +10,16 @@ class IPAMConfig(AppConfig):
|
|||||||
def ready(self):
|
def ready(self):
|
||||||
from netbox.models.features import register_models
|
from netbox.models.features import register_models
|
||||||
from . import signals, search # noqa: F401
|
from . import signals, search # noqa: F401
|
||||||
|
from .models import Prefix
|
||||||
|
|
||||||
# Register models
|
# Register models
|
||||||
register_models(*self.get_models())
|
register_models(*self.get_models())
|
||||||
|
|
||||||
|
# Register denormalized fields
|
||||||
|
denormalized.register(Prefix, '_site', {
|
||||||
|
'_region': 'region',
|
||||||
|
'_sitegroup': 'group',
|
||||||
|
})
|
||||||
|
denormalized.register(Prefix, '_location', {
|
||||||
|
'_site': 'site',
|
||||||
|
})
|
||||||
|
@ -23,7 +23,7 @@ class Migration(migrations.Migration):
|
|||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
# Add the scope GenericForeignKey
|
# Add the `scope` GenericForeignKey
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='prefix',
|
model_name='prefix',
|
||||||
name='scope_id',
|
name='scope_id',
|
||||||
@ -46,10 +46,4 @@ class Migration(migrations.Migration):
|
|||||||
code=copy_site_assignments,
|
code=copy_site_assignments,
|
||||||
reverse_code=migrations.RunPython.noop
|
reverse_code=migrations.RunPython.noop
|
||||||
),
|
),
|
||||||
|
|
||||||
# Delete the site ForeignKey
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='prefix',
|
|
||||||
name='site',
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
61
netbox/ipam/migrations/0072_prefix_cached_relations.py
Normal file
61
netbox/ipam/migrations/0072_prefix_cached_relations.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
def populate_denormalized_fields(apps, schema_editor):
|
||||||
|
"""
|
||||||
|
Copy site ForeignKey values to the scope GFK.
|
||||||
|
"""
|
||||||
|
Prefix = apps.get_model('ipam', 'Prefix')
|
||||||
|
|
||||||
|
prefixes = Prefix.objects.filter(site__isnull=False).prefetch_related('site')
|
||||||
|
for prefix in prefixes:
|
||||||
|
prefix._region_id = prefix.site.region_id
|
||||||
|
prefix._sitegroup_id = prefix.site.group_id
|
||||||
|
prefix._site_id = prefix.site_id
|
||||||
|
# Note: Location cannot be set prior to migration
|
||||||
|
|
||||||
|
Prefix.objects.bulk_update(prefixes, ['_region', '_sitegroup', '_site'])
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0192_poweroutlet_color'),
|
||||||
|
('ipam', '0071_prefix_scope'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='prefix',
|
||||||
|
name='_location',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.location'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='prefix',
|
||||||
|
name='_region',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.region'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='prefix',
|
||||||
|
name='_site',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.site'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='prefix',
|
||||||
|
name='_sitegroup',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.sitegroup'),
|
||||||
|
),
|
||||||
|
|
||||||
|
# Populate denormalized FK values
|
||||||
|
migrations.RunPython(
|
||||||
|
code=populate_denormalized_fields,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
|
||||||
|
# Delete the site ForeignKey
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='prefix',
|
||||||
|
name='site',
|
||||||
|
),
|
||||||
|
]
|
@ -1,4 +1,5 @@
|
|||||||
import netaddr
|
import netaddr
|
||||||
|
from django.apps import apps
|
||||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
@ -270,6 +271,32 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, PrimaryModel):
|
|||||||
help_text=_("Treat as fully utilized")
|
help_text=_("Treat as fully utilized")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Cached associations to enable efficient filtering
|
||||||
|
_location = models.ForeignKey(
|
||||||
|
to='dcim.Location',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
_site = models.ForeignKey(
|
||||||
|
to='dcim.Site',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
_region = models.ForeignKey(
|
||||||
|
to='dcim.Region',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
_sitegroup = models.ForeignKey(
|
||||||
|
to='dcim.SiteGroup',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
# Cached depth & child counts
|
# Cached depth & child counts
|
||||||
_depth = models.PositiveSmallIntegerField(
|
_depth = models.PositiveSmallIntegerField(
|
||||||
default=0,
|
default=0,
|
||||||
@ -331,8 +358,37 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, PrimaryModel):
|
|||||||
# Clear host bits from prefix
|
# Clear host bits from prefix
|
||||||
self.prefix = self.prefix.cidr
|
self.prefix = self.prefix.cidr
|
||||||
|
|
||||||
|
# Cache objects associated with the terminating object (for filtering)
|
||||||
|
self.cache_related_objects()
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
def cache_related_objects(self):
|
||||||
|
if self.scope is None:
|
||||||
|
return
|
||||||
|
scope_type = self.scope_type.model_class()
|
||||||
|
if scope_type == apps.get_model('dcim', 'region'):
|
||||||
|
self._region = self.scope
|
||||||
|
self._sitegroup = None
|
||||||
|
self._site = None
|
||||||
|
self._location = None
|
||||||
|
elif scope_type == apps.get_model('dcim', 'sitegroup'):
|
||||||
|
self._region = None
|
||||||
|
self._sitegroup = self.scope
|
||||||
|
self._site = None
|
||||||
|
self._location = None
|
||||||
|
elif scope_type == apps.get_model('dcim', 'site'):
|
||||||
|
self._region = self.scope.region
|
||||||
|
self._sitegroup = self.scope.group
|
||||||
|
self._site = self.scope
|
||||||
|
self._location = None
|
||||||
|
elif scope_type == apps.get_model('dcim', 'location'):
|
||||||
|
self._region = self.scope.site.region
|
||||||
|
self._sitegroup = self.scope.site.group
|
||||||
|
self._site = self.scope.site
|
||||||
|
self._location = self.scope
|
||||||
|
cache_related_objects.alters_data = True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def family(self):
|
def family(self):
|
||||||
return self.prefix.version if self.prefix else None
|
return self.prefix.version if self.prefix else None
|
||||||
|
Loading…
Reference in New Issue
Block a user