mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-28 10:16:10 -06:00
* Fixes #19321: Reduce redundant database queries during bulk creation of devices * Add test for test_get_prefetchable_fields
This commit is contained in:
parent
1b8767f1e3
commit
a20715f229
@ -8,7 +8,7 @@ from django.core.exceptions import ValidationError
|
|||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import F, ProtectedError
|
from django.db.models import F, ProtectedError, prefetch_related_objects
|
||||||
from django.db.models.functions import Lower
|
from django.db.models.functions import Lower
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
@ -28,6 +28,7 @@ from netbox.models import NestedGroupModel, OrganizationalModel, PrimaryModel
|
|||||||
from netbox.models.mixins import WeightMixin
|
from netbox.models.mixins import WeightMixin
|
||||||
from netbox.models.features import ContactsMixin, ImageAttachmentsMixin
|
from netbox.models.features import ContactsMixin, ImageAttachmentsMixin
|
||||||
from utilities.fields import ColorField, CounterCacheField
|
from utilities.fields import ColorField, CounterCacheField
|
||||||
|
from utilities.prefetch import get_prefetchable_fields
|
||||||
from utilities.tracking import TrackingModelMixin
|
from utilities.tracking import TrackingModelMixin
|
||||||
from .device_components import *
|
from .device_components import *
|
||||||
from .mixins import RenderConfigMixin
|
from .mixins import RenderConfigMixin
|
||||||
@ -924,7 +925,10 @@ class Device(
|
|||||||
if cf_defaults := CustomField.objects.get_defaults_for_model(model):
|
if cf_defaults := CustomField.objects.get_defaults_for_model(model):
|
||||||
for component in components:
|
for component in components:
|
||||||
component.custom_field_data = cf_defaults
|
component.custom_field_data = cf_defaults
|
||||||
model.objects.bulk_create(components)
|
components = model.objects.bulk_create(components)
|
||||||
|
# Prefetch related objects to minimize queries needed during post_save
|
||||||
|
prefetch_fields = get_prefetchable_fields(model)
|
||||||
|
prefetch_related_objects(components, *prefetch_fields)
|
||||||
# Manually send the post_save signal for each of the newly created components
|
# Manually send the post_save signal for each of the newly created components
|
||||||
for component in components:
|
for component in components:
|
||||||
post_save.send(
|
post_save.send(
|
||||||
|
34
netbox/utilities/prefetch.py
Normal file
34
netbox/utilities/prefetch.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
from django.contrib.contenttypes.fields import GenericRelation
|
||||||
|
from django.db.models import ManyToManyField
|
||||||
|
from django.db.models.fields.related import ForeignObjectRel
|
||||||
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'get_prefetchable_fields',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_prefetchable_fields(model):
|
||||||
|
"""
|
||||||
|
Return a list containing the names of all fields on the given model which support prefetching.
|
||||||
|
"""
|
||||||
|
field_names = []
|
||||||
|
|
||||||
|
for field in model._meta.get_fields():
|
||||||
|
# Forward relations (e.g. ManyToManyFields)
|
||||||
|
if isinstance(field, ManyToManyField):
|
||||||
|
field_names.append(field.name)
|
||||||
|
|
||||||
|
# Reverse relations (e.g. reverse ForeignKeys, reverse M2M)
|
||||||
|
elif isinstance(field, ForeignObjectRel):
|
||||||
|
field_names.append(field.get_accessor_name())
|
||||||
|
|
||||||
|
# Generic relations
|
||||||
|
elif isinstance(field, GenericRelation):
|
||||||
|
field_names.append(field.name)
|
||||||
|
|
||||||
|
# Tags
|
||||||
|
elif isinstance(field, TaggableManager):
|
||||||
|
field_names.append(field.name)
|
||||||
|
|
||||||
|
return field_names
|
17
netbox/utilities/tests/test_prefetch.py
Normal file
17
netbox/utilities/tests/test_prefetch.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
from circuits.models import Circuit, Provider
|
||||||
|
from utilities.prefetch import get_prefetchable_fields
|
||||||
|
from utilities.testing.base import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class GetPrefetchableFieldsTest(TestCase):
|
||||||
|
"""
|
||||||
|
Verify the operation of get_prefetchable_fields()
|
||||||
|
"""
|
||||||
|
def test_get_prefetchable_fields(self):
|
||||||
|
field_names = get_prefetchable_fields(Provider)
|
||||||
|
self.assertIn('asns', field_names) # ManyToManyField
|
||||||
|
self.assertIn('circuits', field_names) # Reverse relation
|
||||||
|
self.assertIn('tags', field_names) # Tags
|
||||||
|
|
||||||
|
field_names = get_prefetchable_fields(Circuit)
|
||||||
|
self.assertIn('group_assignments', field_names) # Generic relation
|
Loading…
Reference in New Issue
Block a user