Skip to content

Commit 2e931e0

Browse files
authored
Use /data as the pointer for non-field serializer errors (#1137)
1 parent ad5a793 commit 2e931e0

File tree

6 files changed

+54
-3
lines changed

6 files changed

+54
-3
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Adam Ziolkowski <[email protected]>
33
Alan Crosswell <[email protected]>
44
Alex Seidmann <[email protected]>
55
Anton Shutik <[email protected]>
6+
Arttu Perälä <[email protected]>
67
Ashley Loewen <[email protected]>
78
Asif Saif Uddin <[email protected]>
89
Beni Keller <[email protected]>

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ any parts of the framework not mentioned in the documentation should generally b
2323
### Fixed
2424

2525
* Refactored handling of the `sort` query parameter to fix duplicate declaration in the generated schema definition
26+
* Non-field serializer errors are given a source.pointer value of "/data".
2627

2728
## [6.0.0] - 2022-09-24
2829

example/api/serializers/identity.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ def validate_last_name(self, data):
2323
)
2424
return data
2525

26+
def validate(self, data):
27+
if data["first_name"] == data["last_name"]:
28+
raise serializers.ValidationError(
29+
"First name cannot be the same as last name!"
30+
)
31+
return data
32+
2633
class Meta:
2734
model = auth_models.User
2835
fields = (

example/tests/test_generic_viewset.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,35 @@ def test_custom_validation_exceptions(self):
129129
)
130130

131131
assert expected == response.json()
132+
133+
def test_nonfield_validation_exceptions(self):
134+
"""
135+
Non-field errors should be attributed to /data source.pointer.
136+
"""
137+
expected = {
138+
"errors": [
139+
{
140+
"status": "400",
141+
"source": {
142+
"pointer": "/data",
143+
},
144+
"detail": "First name cannot be the same as last name!",
145+
"code": "invalid",
146+
},
147+
]
148+
}
149+
response = self.client.post(
150+
"/identities",
151+
{
152+
"data": {
153+
"type": "users",
154+
"attributes": {
155+
"email": "[email protected]",
156+
"first_name": "Miles",
157+
"last_name": "Miles",
158+
},
159+
}
160+
},
161+
)
162+
163+
assert expected == response.json()

rest_framework_json_api/serializers.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,12 @@ def validate_path(serializer_class, field_path, path):
155155
class ReservedFieldNamesMixin:
156156
"""Ensures that reserved field names are not used and an error raised instead."""
157157

158-
_reserved_field_names = {"meta", "results", "type"}
158+
_reserved_field_names = {
159+
"meta",
160+
"results",
161+
"type",
162+
api_settings.NON_FIELD_ERRORS_KEY,
163+
}
159164

160165
def get_fields(self):
161166
fields = super().get_fields()

rest_framework_json_api/utils.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from django.utils.translation import gettext_lazy as _
1414
from rest_framework import exceptions, relations
1515
from rest_framework.exceptions import APIException
16+
from rest_framework.settings import api_settings
1617

1718
from .settings import json_api_settings
1819

@@ -381,10 +382,14 @@ def format_drf_errors(response, context, exc):
381382
]
382383

383384
for field, error in response.data.items():
385+
non_field_error = field == api_settings.NON_FIELD_ERRORS_KEY
384386
field = format_field_name(field)
385387
pointer = None
386-
# pointer can be determined only if there's a serializer.
387-
if has_serializer:
388+
if non_field_error:
389+
# Serializer error does not refer to a specific field.
390+
pointer = "/data"
391+
elif has_serializer:
392+
# pointer can be determined only if there's a serializer.
388393
rel = "relationships" if field in relationship_fields else "attributes"
389394
pointer = f"/data/{rel}/{field}"
390395
if isinstance(exc, Http404) and isinstance(error, str):

0 commit comments

Comments
 (0)