Skip to content
Merged
Changes from 2 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
36 changes: 24 additions & 12 deletions rest_framework/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,18 @@ def __new__(cls, name, bases, attrs):


def as_serializer_error(exc):
"""
Coerce validation exceptions into a standardized serialized error format.

This function normalizes both Django's `ValidationError` and REST
framework's `ValidationError` into a dictionary structure compatible
with serializer `.errors`, ensuring all values are represented as
lists of error details.

The returned structure conforms to the serializer error contract:
- Field-specific errors are returned as '{field-name: [errors]}'
- Non-field errors are returned under the 'NON_FIELD_ERRORS_KEY'
"""
assert isinstance(exc, (ValidationError, DjangoValidationError))

if isinstance(exc, DjangoValidationError):
Expand Down Expand Up @@ -815,22 +827,22 @@ def errors(self):

def raise_errors_on_nested_writes(method_name, serializer, validated_data):
"""
Give explicit errors when users attempt to pass writable nested data.

If we don't do this explicitly they'd get a less helpful error when
calling `.save()` on the serializer.
Enforce explicit handling of writable nested and dotted-source fields.

We don't *automatically* support these sorts of nested writes because
there are too many ambiguities to define a default behavior.
This helper raises clear and actionable errors when a serializer attempts
to perform writable nested updates or creates using the default
`ModelSerializer` behavior.

Eg. Suppose we have a `UserSerializer` with a nested profile. How should
we handle the case of an update, where the `profile` relationship does
not exist? Any of the following might be valid:
Writable nested relationships and dotted-source fields are intentionally
unsupported by default due to ambiguous persistence semantics. Developers
must either:
- Override the `.create()` / `.update()` methods explicitly, or
- Mark nested serializers as `read_only=True`

* Raise an application error.
* Silently ignore the nested part of the update.
* Automatically create a profile instance.
This check is invoked internally by default `ModelSerializer.create()`
and `ModelSerializer.update()` implementations.
Comment on lines -818 to +843
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this one... The previous version seemed simpler and clearer to me. The updated versions is more abstract and lacks the concrete example that was present before, which illustrates the "why" pretty well.

Not sure what other maintainers think, but I'd be in favour of reverting this part. Thoughts?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@browniebroke Thanks for the feedback — that’s fair.

My intent with the rewrite was to make the docstring more explicit about what guarantees the helper provides and why writable nested writes are intentionally unsupported, especially for readers coming to this code without prior DRF context. That said, I agree the earlier concrete example does a very good job of illustrating the ambiguity in a way that’s immediately intuitive.

I’m happy to revert this section, or alternatively reintroduce the concrete example alongside the more structured explanation if that feels like a better balance. I don’t have a strong preference either way and would defer to what maintainers feel best fits the project’s documentation style.

Let me know what you’d prefer and I’ll update the PR accordingly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion. Let's re-introduce the concrete example to illustrate, and then I'm hppy to accept it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your insight, could you please verify this?

"""

ModelClass = serializer.Meta.model
model_field_info = model_meta.get_field_info(ModelClass)

Expand Down
Loading