Skip to content

Commit

Permalink
Support save() on list serializers (fixes #75)
Browse files Browse the repository at this point in the history
  • Loading branch information
oxan committed Apr 28, 2023
1 parent c858358 commit 43c2fd7
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 0 deletions.
38 changes: 38 additions & 0 deletions rest_framework_dataclasses/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,44 @@ def validated_data(self) -> T:


class DataclassListSerializer(rest_framework.serializers.ListSerializer, Generic[T]):
# Type hints for (private) fields on the parent class
_validated_data: List[T]

def save(self, **kwargs: KWArgs) -> T:
assert hasattr(self, '_errors'), (
"You must call `.is_valid()` before calling `.save()`."
)

assert not self.errors, (
"You cannot call `.save()` on a serializer with invalid data."
)

assert not hasattr(self, '_data'), (
"You cannot call `.save()` after accessing `serializer.data`."
"If you need to access data before committing to the dataclass then "
"inspect 'serializer.validated_data' instead. "
)

# Explicitly use the internal validated_data here, as the empty sentinel values must not be stripped yet. Do not
# use dataclasses.replace(), as it doesn't handle non-init fields properly.
validated_data = []
for item in self._validated_data:
obj = copy.deepcopy(item)
for field, value in kwargs.items():
setattr(obj, field, value)
validated_data.append(obj)

if self.instance is not None:
self.instance = self.update(self.instance, validated_data)
else:
self.instance = self.create(validated_data)

assert self.instance is not None, (
'`update()` or `create()` did not return an object instance.'
)

return self.instance

@cached_property
def validated_data(self) -> List[T]:
"""
Expand Down
15 changes: 15 additions & 0 deletions tests/test_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,18 @@ class A:

self.assertEqual(instance.foo, 'abc')
self.assertEqual(instance.bar, 'def')

# Issue #75: save() on list serializer doesn't work
def test_list_save(self):
@dataclasses.dataclass
class Foo:
name: str

serializer = DataclassSerializer(dataclass=Foo, many=True, data=[{'name': 'bar'}, {'name': 'baz'}])
serializer.is_valid(raise_exception=True)
items = serializer.save()

self.assertIsInstance(items, list)
self.assertEqual(len(items), 2)
self.assertEqual(items[0], Foo('bar'))
self.assertEqual(items[1], Foo('baz'))

0 comments on commit 43c2fd7

Please sign in to comment.