Skip to content

Commit

Permalink
#10694: Emit post_save signal when creating/updating device component…
Browse files Browse the repository at this point in the history
…s in bulk (#10900)

* Emit post_save signal when creating/updating device components in bulk

* Fix post_save for bulk_update()
  • Loading branch information
jeremystretch authored Nov 14, 2022
1 parent 6eba5d4 commit a57c937
Showing 1 changed file with 64 additions and 33 deletions.
97 changes: 64 additions & 33 deletions netbox/dcim/models/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from django.db import models
from django.db.models import F, ProtectedError
from django.db.models.functions import Lower
from django.db.models.signals import post_save
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _
Expand Down Expand Up @@ -769,6 +770,32 @@ def clean(self):
'vc_position': "A device assigned to a virtual chassis must have its position defined."
})

def _instantiate_components(self, queryset, bulk_create=True):
"""
Instantiate components for the device from the specified component templates.
Args:
bulk_create: If True, bulk_create() will be called to create all components in a single query
(default). Otherwise, save() will be called on each instance individually.
"""
components = [obj.instantiate(device=self) for obj in queryset]
if components and bulk_create:
model = components[0]._meta.model
model.objects.bulk_create(components)
# Manually send the post_save signal for each of the newly created components
for component in components:
post_save.send(
sender=model,
instance=component,
created=True,
raw=False,
using='default',
update_fields=None
)
elif components:
for component in components:
component.save()

def save(self, *args, **kwargs):
is_new = not bool(self.pk)

Expand All @@ -778,38 +805,19 @@ def save(self, *args, **kwargs):

super().save(*args, **kwargs)

# If this is a new Device, instantiate all of the related components per the DeviceType definition
# If this is a new Device, instantiate all the related components per the DeviceType definition
if is_new:
ConsolePort.objects.bulk_create(
[x.instantiate(device=self) for x in self.device_type.consoleporttemplates.all()]
)
ConsoleServerPort.objects.bulk_create(
[x.instantiate(device=self) for x in self.device_type.consoleserverporttemplates.all()]
)
PowerPort.objects.bulk_create(
[x.instantiate(device=self) for x in self.device_type.powerporttemplates.all()]
)
PowerOutlet.objects.bulk_create(
[x.instantiate(device=self) for x in self.device_type.poweroutlettemplates.all()]
)
Interface.objects.bulk_create(
[x.instantiate(device=self) for x in self.device_type.interfacetemplates.all()]
)
RearPort.objects.bulk_create(
[x.instantiate(device=self) for x in self.device_type.rearporttemplates.all()]
)
FrontPort.objects.bulk_create(
[x.instantiate(device=self) for x in self.device_type.frontporttemplates.all()]
)
ModuleBay.objects.bulk_create(
[x.instantiate(device=self) for x in self.device_type.modulebaytemplates.all()]
)
DeviceBay.objects.bulk_create(
[x.instantiate(device=self) for x in self.device_type.devicebaytemplates.all()]
)
# Avoid bulk_create to handle MPTT
for x in self.device_type.inventoryitemtemplates.all():
x.instantiate(device=self).save()
self._instantiate_components(self.device_type.consoleporttemplates.all())
self._instantiate_components(self.device_type.consoleserverporttemplates.all())
self._instantiate_components(self.device_type.powerporttemplates.all())
self._instantiate_components(self.device_type.poweroutlettemplates.all())
self._instantiate_components(self.device_type.interfacetemplates.all())
self._instantiate_components(self.device_type.rearporttemplates.all())
self._instantiate_components(self.device_type.frontporttemplates.all())
self._instantiate_components(self.device_type.modulebaytemplates.all())
self._instantiate_components(self.device_type.devicebaytemplates.all())
# Disable bulk_create to accommodate MPTT
self._instantiate_components(self.device_type.inventoryitemtemplates.all(), bulk_create=False)

# Update Site and Rack assignment for any child Devices
devices = Device.objects.filter(parent_bay__device=self)
Expand Down Expand Up @@ -980,7 +988,8 @@ def save(self, *args, **kwargs):

# Prefetch installed components
installed_components = {
component.name: component for component in getattr(self.device, component_attribute).filter(module__isnull=True)
component.name: component
for component in getattr(self.device, component_attribute).filter(module__isnull=True)
}

# Get the template for the module type.
Expand All @@ -1002,7 +1011,29 @@ def save(self, *args, **kwargs):
create_instances.append(template_instance)

component_model.objects.bulk_create(create_instances)
component_model.objects.bulk_update(update_instances, ['module'])
# Emit the post_save signal for each newly created object
for component in create_instances:
post_save.send(
sender=component_model,
instance=component,
created=True,
raw=False,
using='default',
update_fields=None
)

update_fields = ['module']
component_model.objects.bulk_update(update_instances, update_fields)
# Emit the post_save signal for each updated object
for component in update_instances:
post_save.send(
sender=component_model,
instance=component,
created=False,
raw=False,
using='default',
update_fields=update_fields
)


#
Expand Down

0 comments on commit a57c937

Please sign in to comment.