From d58604f273485d0a44d5f32c419d3ce5807813f6 Mon Sep 17 00:00:00 2001 From: Melvin Koh Date: Mon, 23 Dec 2024 05:54:48 -0500 Subject: [PATCH] Add support for dynamic serializer fields (#109) --- drf_excel/renderers.py | 4 ++-- tests/test_viewset_mixin.py | 16 ++++++++++++++++ tests/testapp/serializers.py | 12 ++++++++++++ tests/testapp/views.py | 29 +++++++++++++++++++++++++++-- tests/urls.py | 8 +++++++- 5 files changed, 64 insertions(+), 5 deletions(-) diff --git a/drf_excel/renderers.py b/drf_excel/renderers.py index ba29313..002d7ba 100644 --- a/drf_excel/renderers.py +++ b/drf_excel/renderers.py @@ -241,7 +241,7 @@ def _check_validation_data(self, data): def _serializer_fields(self, serializer, parent_key="", key_sep="."): _fields_dict = {} - for k, v in serializer.get_fields().items(): + for k, v in serializer.fields.items(): new_key = f"{parent_key}{key_sep}{k}" if parent_key else k if isinstance(v, Serializer): _fields_dict.update(self._serializer_fields(v, new_key, key_sep)) @@ -274,7 +274,7 @@ def _get_label(parent_label, label_sep, obj): return False _header_dict = {} - _fields = serializer.get_fields() + _fields = serializer.fields for k, v in _fields.items(): new_key = f"{parent_key}{key_sep}{k}" if parent_key else k # Skip headers we want to ignore diff --git a/tests/test_viewset_mixin.py b/tests/test_viewset_mixin.py index 7799258..31b2911 100644 --- a/tests/test_viewset_mixin.py +++ b/tests/test_viewset_mixin.py @@ -110,3 +110,19 @@ def test_secret_field_viewset(api_client, workbook_reader): # Check that the secret field is not included in the header or data assert [col.value for col in header] == ["title"] assert [col.value for col in data] == ["foo"] + + +def test_dynamic_field_viewset(api_client, workbook_reader): + response = api_client.get("/dynamic-field/") + assert response.status_code == 200 + + wb = workbook_reader(response.content) + sheet = wb.worksheets[0] + + header, data = list(sheet.rows) + + header_values = [cell.value for cell in header] + assert header_values == ["field_1", "field_2", "field_99", "field_98"] + + row_1_values = [cell.value for cell in data] + assert row_1_values == ["YUL", "CDG", "YYZ", "MAR"] diff --git a/tests/testapp/serializers.py b/tests/testapp/serializers.py index e433822..e3f70b6 100644 --- a/tests/testapp/serializers.py +++ b/tests/testapp/serializers.py @@ -33,3 +33,15 @@ class Meta: fields = ("title", "secret", "secret_external") extra_kwargs = {"secret": {"write_only": True}} + + +class DynamicFieldSerializer(serializers.Serializer): + field_1 = serializers.CharField() + field_2 = serializers.CharField() + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Fields can be added dynamically + self.fields["field_99"] = serializers.CharField() + self.fields["field_98"] = serializers.CharField() diff --git a/tests/testapp/views.py b/tests/testapp/views.py index 71b7038..77c43a8 100644 --- a/tests/testapp/views.py +++ b/tests/testapp/views.py @@ -1,10 +1,16 @@ -from rest_framework.viewsets import ReadOnlyModelViewSet +from rest_framework.response import Response +from rest_framework.viewsets import GenericViewSet, ReadOnlyModelViewSet from drf_excel.mixins import XLSXFileMixin from drf_excel.renderers import XLSXRenderer from .models import AllFieldsModel, ExampleModel, SecretFieldModel -from .serializers import AllFieldsSerializer, ExampleSerializer, SecretFieldSerializer +from .serializers import ( + AllFieldsSerializer, + DynamicFieldSerializer, + ExampleSerializer, + SecretFieldSerializer, +) class ExampleViewSet(XLSXFileMixin, ReadOnlyModelViewSet): @@ -26,3 +32,22 @@ class SecretFieldViewSet(XLSXFileMixin, ReadOnlyModelViewSet): serializer_class = SecretFieldSerializer renderer_classes = (XLSXRenderer,) filename = "secret.xlsx" + + +class DynamicFieldViewSet(XLSXFileMixin, GenericViewSet): + serializer_class = DynamicFieldSerializer + renderer_classes = (XLSXRenderer,) + filename = "dynamic_field.xlsx" + + def list(self, request, *args, **kwargs): + serializer = self.get_serializer( + data={ + "field_1": "YUL", + "field_2": "CDG", + "field_55": "LHR", + "field_98": "MAR", + "field_99": "YYZ", + } + ) + serializer.is_valid(raise_exception=True) + return Response(serializer.data) diff --git a/tests/urls.py b/tests/urls.py index d875c66..8eadca0 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,10 +1,16 @@ from rest_framework import routers -from .testapp.views import AllFieldsViewSet, ExampleViewSet, SecretFieldViewSet +from .testapp.views import ( + AllFieldsViewSet, + DynamicFieldViewSet, + ExampleViewSet, + SecretFieldViewSet, +) router = routers.SimpleRouter() router.register(r"examples", ExampleViewSet) router.register(r"all-fields", AllFieldsViewSet) router.register(r"secret-field", SecretFieldViewSet) +router.register(r"dynamic-field", DynamicFieldViewSet, basename="dynamic-field") urlpatterns = router.urls