perf(api): Prefetch reverse many-to-many serializer fields (#23064)
Recognize Django's ManyToManyRel in get_prefetches_for_serializer(). Because it is a sibling of ManyToOneRel rather than a subclass, reverse many-to-many accessors were omitted from the generated prefetch paths and fetched once per serialized object. Add regression coverage for both automatically generated fields and SerializedPKRelatedField(many=True), and regenerate the affected ASN and ObjectPermission API query-count baselines. Fixes #23060
This commit is contained in:
parent
0f22d67617
commit
2b3b9e62e7
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"aggregate:api_list_objects": 13,
|
||||
"aggregate:list_objects_with_permission": 21,
|
||||
"asn:api_list_objects": 17,
|
||||
"asn:api_list_objects": 16,
|
||||
"asn:list_objects_with_permission": 28,
|
||||
"asnrange:api_list_objects": 14,
|
||||
"asnrange:list_objects_with_permission": 19,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"group:api_list_objects": 10,
|
||||
"group:list_objects_with_permission": 16,
|
||||
"objectpermission:api_list_objects": 14,
|
||||
"objectpermission:api_list_objects": 12,
|
||||
"objectpermission:list_objects_with_permission": 17,
|
||||
"owner:api_list_objects": 11,
|
||||
"owner:list_objects_with_permission": 18,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from django.core.exceptions import (
|
|||
ObjectDoesNotExist,
|
||||
ValidationError,
|
||||
)
|
||||
from django.db.models.fields.related import ManyToOneRel, RelatedField
|
||||
from django.db.models.fields.related import ManyToManyRel, ManyToOneRel, RelatedField
|
||||
from django.urls import reverse
|
||||
from django.utils.module_loading import import_string
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
|
@ -193,7 +193,7 @@ def get_prefetches_for_serializer(serializer_class, fields=None, omit=None, _ser
|
|||
# If the serializer field does not map to a discrete model field, skip it.
|
||||
try:
|
||||
field = model._meta.get_field(model_field_name)
|
||||
if isinstance(field, (RelatedField, ManyToOneRel, GenericForeignKey)):
|
||||
if isinstance(field, (RelatedField, ManyToOneRel, ManyToManyRel, GenericForeignKey)):
|
||||
prefetch_fields.append(field.name)
|
||||
except FieldDoesNotExist:
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ from netbox.api.serializers import BaseModelSerializer
|
|||
from netbox.config import get_config
|
||||
from netbox.plugins import register_serializer_resolver
|
||||
from netbox.registry import registry
|
||||
from users.models import ObjectPermission
|
||||
from users.models import Group, ObjectPermission
|
||||
from utilities.api import (
|
||||
get_prefetches_for_serializer,
|
||||
get_serializer_for_model,
|
||||
|
|
@ -754,6 +754,41 @@ class GetPrefetchesForSerializerTestCase(TestCase):
|
|||
['parent', 'parent__sites', 'children', 'children__sites'],
|
||||
)
|
||||
|
||||
def test_reverse_many_to_many_relation_is_prefetched(self):
|
||||
class ObjectPermissionSerializer(BaseModelSerializer):
|
||||
class Meta:
|
||||
model = ObjectPermission
|
||||
fields = ('groups',)
|
||||
|
||||
self.assertListEqual(
|
||||
get_prefetches_for_serializer(ObjectPermissionSerializer),
|
||||
['groups'],
|
||||
)
|
||||
|
||||
def test_reverse_many_to_many_serialized_related_field_is_prefetched(self):
|
||||
class GroupSerializer(BaseModelSerializer):
|
||||
class Meta:
|
||||
model = Group
|
||||
fields = ('id', 'name')
|
||||
|
||||
class ObjectPermissionSerializer(BaseModelSerializer):
|
||||
groups = SerializedPKRelatedField(
|
||||
queryset=Group.objects.all(),
|
||||
serializer=GroupSerializer,
|
||||
nested=True,
|
||||
required=False,
|
||||
many=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ObjectPermission
|
||||
fields = ('id', 'groups')
|
||||
|
||||
self.assertListEqual(
|
||||
get_prefetches_for_serializer(ObjectPermissionSerializer),
|
||||
['groups'],
|
||||
)
|
||||
|
||||
|
||||
class _ResolvedSerializerA(Serializer):
|
||||
pass
|
||||
|
|
|
|||
Loading…
Reference in New Issue