Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance nested serializer handling by skipping write_only fields. #100

Merged
merged 4 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions drf_excel/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ def _flatten_serializer_keys(
use_labels=False,
):
"""
Iterate through serializer fields recursively when field is a nested serializer.
Iterate through serializer fields recursively when field is a nested serializer. Skip write_only fields.
"""

def _get_label(parent_label, label_sep, obj):
Expand All @@ -278,7 +278,7 @@ def _get_label(parent_label, label_sep, obj):
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
if new_key in self.ignore_headers:
if new_key in self.ignore_headers or getattr(v, "write_only", False):
continue
# Iterate through fields if field is a serializer. Check for labels and
# append if `use_labels` is True. Fallback to using keys.
Expand Down
19 changes: 18 additions & 1 deletion tests/test_viewset_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from rest_framework.test import APIClient
from time_machine import TimeMachineFixture

from tests.testapp.models import AllFieldsModel, ExampleModel, Tag
from tests.testapp.models import AllFieldsModel, ExampleModel, SecretFieldModel, Tag

pytestmark = pytest.mark.django_db

Expand Down Expand Up @@ -93,3 +93,20 @@ def test_all_fields_viewset(
True,
"test, example",
]


def test_secret_field_viewset(api_client, workbook_reader):
SecretFieldModel.objects.create(title="foo", secret="bar")

response = api_client.get("/secret-field/")
assert response.status_code == 200

wb = workbook_reader(response.content)
sheet = wb.worksheets[0]
rows = list(sheet.rows)
assert len(rows) == 2
header, data = rows

# 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"]
8 changes: 8 additions & 0 deletions tests/testapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,11 @@ def __str__(self):

def get_tag_names(self):
return [tag.name for tag in self.tags.all()]


class SecretFieldModel(models.Model):
title = models.CharField(max_length=100)
secret = models.CharField(max_length=100)

def __str__(self):
return self.title
12 changes: 11 additions & 1 deletion tests/testapp/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from rest_framework import serializers

from .models import AllFieldsModel, ExampleModel
from .models import AllFieldsModel, ExampleModel, SecretFieldModel


class ExampleSerializer(serializers.ModelSerializer):
Expand All @@ -23,3 +23,13 @@ class Meta:
"is_active",
"tags",
)


class SecretFieldSerializer(serializers.ModelSerializer):
secret_external = serializers.CharField(write_only=True)

class Meta:
model = SecretFieldModel
fields = ("title", "secret", "secret_external")

extra_kwargs = {"secret": {"write_only": True}}
11 changes: 9 additions & 2 deletions tests/testapp/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from drf_excel.mixins import XLSXFileMixin
from drf_excel.renderers import XLSXRenderer

from .models import AllFieldsModel, ExampleModel
from .serializers import AllFieldsSerializer, ExampleSerializer
from .models import AllFieldsModel, ExampleModel, SecretFieldModel
from .serializers import AllFieldsSerializer, ExampleSerializer, SecretFieldSerializer


class ExampleViewSet(XLSXFileMixin, ReadOnlyModelViewSet):
Expand All @@ -19,3 +19,10 @@ class AllFieldsViewSet(XLSXFileMixin, ReadOnlyModelViewSet):
serializer_class = AllFieldsSerializer
renderer_classes = (XLSXRenderer,)
filename = "al_fileds.xlsx"


class SecretFieldViewSet(XLSXFileMixin, ReadOnlyModelViewSet):
queryset = SecretFieldModel.objects.all()
serializer_class = SecretFieldSerializer
renderer_classes = (XLSXRenderer,)
filename = "secret.xlsx"
3 changes: 2 additions & 1 deletion tests/urls.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from rest_framework import routers

from .testapp.views import AllFieldsViewSet, ExampleViewSet
from .testapp.views import AllFieldsViewSet, ExampleViewSet, SecretFieldViewSet

router = routers.SimpleRouter()
router.register(r"examples", ExampleViewSet)
router.register(r"all-fields", AllFieldsViewSet)
router.register(r"secret-field", SecretFieldViewSet)

urlpatterns = router.urls