Enable dynamic field inclusion for REST API serializers

This commit is contained in:
Jeremy Stretch 2024-02-12 13:31:51 -05:00
parent f41105d5e3
commit d99928435a
2 changed files with 47 additions and 1 deletions

View File

@ -12,6 +12,15 @@ __all__ = (
class BaseModelSerializer(serializers.ModelSerializer):
display = serializers.SerializerMethodField(read_only=True)
def __init__(self, *args, requested_fields=None, **kwargs):
super().__init__(*args, **kwargs)
# If specific fields have been requested, omit the others
if requested_fields:
for field in list(self.fields.keys()):
if field not in requested_fields:
self.fields.pop(field)
@extend_schema_field(OpenApiTypes.STR)
def get_display(self, obj):
return str(obj)

View File

@ -1,8 +1,11 @@
import logging
from functools import cached_property
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.contrib.contenttypes.fields import GenericForeignKey
from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist, PermissionDenied
from django.db import transaction
from django.db.models import ProtectedError, RestrictedError
from django.db.models.fields.related import ManyToOneRel, RelatedField
from django_pglocks import advisory_lock
from netbox.constants import ADVISORY_LOCK_KEYS
from rest_framework import mixins as drf_mixins
@ -40,6 +43,40 @@ class BaseViewSet(GenericViewSet):
if action := HTTP_ACTIONS[request.method]:
self.queryset = self.queryset.restrict(request.user, action)
def get_queryset(self):
qs = super().get_queryset()
# Dynamically resolve prefetches for included serializer fields and attach them to the queryset
serializer_class = self.get_serializer_class()
model = serializer_class.Meta.model
fields_to_include = self.requested_fields or serializer_class.Meta.fields
prefetch = []
for field_name in fields_to_include:
try:
field = model._meta.get_field(field_name)
except FieldDoesNotExist:
continue
if isinstance(field, (RelatedField, ManyToOneRel, GenericForeignKey)):
# TODO: Use serializer field source if set, else use its name
prefetch.append(field_name)
if prefetch:
qs = qs.prefetch_related(*prefetch)
return qs
def get_serializer(self, *args, **kwargs):
# If specific fields have been requested, pass them to the serializer
if self.requested_fields:
kwargs['requested_fields'] = self.requested_fields
return super().get_serializer(*args, **kwargs)
@cached_property
def requested_fields(self):
requested_fields = self.request.query_params.get('include')
return requested_fields.split(',') if requested_fields else []
class NetBoxReadOnlyModelViewSet(
mixins.BriefModeMixin,