1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Closes #15087: Support for specifying a subset of API serializer fields (#15122)

* Enable dynamic field inclusion for REST API serializers

* Recurse through nested serializer when resolving prefetches

* Remove obsolete calls to prefetch_related() for API views

* Remove support for brief_prefetch_fields viewset attribute

* Rename query parameter

* Fixes #15133: Fix FHRP group representation on assignments endpoint under brief mode (#15134)

* Fixes #15133: Fix FHRP group representation on assignments endpoint under brief mode

* Update API test

* Restore get_queryset() on BriefModeMixin, minus prefetch logic

* get_prefetches_for_serializer() should reference serializer field source if set
This commit is contained in:
Jeremy Stretch
2024-02-14 09:28:37 -05:00
committed by GitHub
parent 72720354df
commit b3f25a400b
16 changed files with 176 additions and 152 deletions

View File

@ -2,9 +2,13 @@ import platform
import sys
from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey
from django.core.exceptions import FieldDoesNotExist
from django.db.models.fields.related import ManyToOneRel, RelatedField
from django.http import JsonResponse
from django.urls import reverse
from rest_framework import status
from rest_framework.serializers import Serializer
from rest_framework.utils import formatting
from netbox.api.exceptions import GraphQLTypeNotFound, SerializerNotFound
@ -12,6 +16,7 @@ from .utils import dynamic_import
__all__ = (
'get_graphql_type_for_model',
'get_prefetches_for_serializer',
'get_serializer_for_model',
'get_view_name',
'is_api_request',
@ -89,6 +94,43 @@ def get_view_name(view, suffix=None):
return name
def get_prefetches_for_serializer(serializer_class, fields_to_include=None):
"""
Compile and return a list of fields which should be prefetched on the queryset for a serializer.
"""
model = serializer_class.Meta.model
# If specific fields are not specified, default to all
if not fields_to_include:
fields_to_include = serializer_class.Meta.fields
prefetch_fields = []
for field_name in fields_to_include:
serializer_field = serializer_class._declared_fields.get(field_name)
# Determine the name of the model field referenced by the serializer field
model_field_name = field_name
if serializer_field and serializer_field.source:
model_field_name = serializer_field.source
# 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)):
prefetch_fields.append(field.name)
except FieldDoesNotExist:
continue
# If this field is represented by a nested serializer, recurse to resolve prefetches
# for the related object.
if serializer_field:
if issubclass(type(serializer_field), Serializer):
for subfield in get_prefetches_for_serializer(type(serializer_field)):
prefetch_fields.append(f'{field_name}__{subfield}')
return prefetch_fields
def rest_api_server_error(request, *args, **kwargs):
"""
Handle exceptions and return a useful error message for REST API requests.