-
Notifications
You must be signed in to change notification settings - Fork 301
WIP: Error handling #787
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
Closed
Closed
WIP: Error handling #787
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
314ea5a
support nested structures
01e399d
Merge pull request #1 from django-json-api/master
bpleshakov aa0d7b9
Merge remote-tracking branch 'origin/master' into patch-3
49c479e
f code style
0b099c2
f ignore deprecation warning
5ddcf69
f cover settings file with test
5a9f1e5
f add changelog
db139e0
+ tests for relations strategy
cf31527
f codestyle
387500a
f included
9071b4f
f wrong check
ab9a1ae
f tests
65f9b15
f pep
67442ab
f rewrite complex drf errors structures into JSON-API format
177bfbc
f move DeprecationWarning from settings to metaclass, which ensures t…
fdc8166
f pep8
72df66f
requested changes
874bd9c
Merge master
873b48e
f unused import
1fddcbe
f progress
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
import pytest | ||
from django.test import override_settings | ||
from django.urls import reverse, path | ||
|
||
from example.models import Blog | ||
from rest_framework_json_api import serializers | ||
from rest_framework import views | ||
|
||
|
||
# serializers | ||
class CommentAttachmentSerializer(serializers.Serializer): | ||
data = serializers.CharField(allow_null=False, required=True) | ||
|
||
def validate_data(self, value): | ||
if value and len(value) < 10: | ||
raise serializers.ValidationError('Too short data') | ||
|
||
|
||
class CommentSerializer(serializers.Serializer): | ||
attachments = CommentAttachmentSerializer(many=True, required=False) | ||
attachment = CommentAttachmentSerializer(required=False) | ||
one_more_attachment = CommentAttachmentSerializer(required=False) | ||
body = serializers.CharField(allow_null=False, required=True) | ||
|
||
|
||
class EntrySerializer(serializers.Serializer): | ||
blog = serializers.IntegerField() | ||
comments = CommentSerializer(many=True, required=False) | ||
comment = CommentSerializer(required=False) | ||
headline = serializers.CharField(allow_null=True, required=True) | ||
body_text = serializers.CharField() | ||
|
||
def validate(self, attrs): | ||
body_text = attrs['body_text'] | ||
if len(body_text) < 5: | ||
raise serializers.ValidationError({'body_text': 'Too short'}) | ||
|
||
|
||
# view | ||
class DummyTestView(views.APIView): | ||
serializer_class = EntrySerializer | ||
resource_name = 'entries' | ||
|
||
def post(self, request, *args, **kwargs): | ||
serializer = self.serializer_class(data=request.data) | ||
serializer.is_valid(raise_exception=True) | ||
|
||
|
||
urlpatterns = [ | ||
path(r'^entries-nested/$', DummyTestView.as_view(), | ||
name='entries-nested-list') | ||
] | ||
|
||
|
||
@pytest.fixture(scope='function') | ||
def some_blog(db): | ||
return Blog.objects.create(name='Some Blog', tagline="It's a blog") | ||
|
||
|
||
def perform_error_test(client, data, expected_pointer, errors_count=1): | ||
with override_settings( | ||
JSON_API_SERIALIZE_NESTED_SERIALIZERS_AS_ATTRIBUTE=True, | ||
ROOT_URLCONF=__name__ | ||
): | ||
url = reverse('entries-nested-list') | ||
response = client.post(url, data=data) | ||
|
||
errors = response.data | ||
|
||
assert len(errors) == errors_count | ||
assert errors[0]['source']['pointer'] == expected_pointer | ||
|
||
|
||
def test_first_level_attribute_error(client, some_blog): | ||
data = { | ||
'data': { | ||
'type': 'entries', | ||
'attributes': { | ||
'blog': some_blog.pk, | ||
'body_text': 'body_text', | ||
} | ||
} | ||
} | ||
perform_error_test(client, data, '/data/attributes/headline') | ||
|
||
|
||
def test_first_level_custom_attribute_error(client, some_blog): | ||
data = { | ||
'data': { | ||
'type': 'entries', | ||
'attributes': { | ||
'blog': some_blog.pk, | ||
'body_text': 'body', | ||
'headline': 'headline' | ||
} | ||
} | ||
} | ||
with override_settings(JSON_API_FORMAT_FIELD_NAMES='underscore'): | ||
perform_error_test(client, data, '/data/attributes/body_text') | ||
|
||
|
||
def test_second_level_array_error(client, some_blog): | ||
data = { | ||
'data': { | ||
'type': 'entries', | ||
'attributes': { | ||
'blog': some_blog.pk, | ||
'body_text': 'body_text', | ||
'headline': 'headline', | ||
'comments': [ | ||
{ | ||
} | ||
] | ||
} | ||
} | ||
} | ||
|
||
perform_error_test(client, data, '/data/attributes/comments/0/body') | ||
|
||
|
||
def test_second_level_dict_error(client, some_blog): | ||
data = { | ||
'data': { | ||
'type': 'entries', | ||
'attributes': { | ||
'blog': some_blog.pk, | ||
'body_text': 'body_text', | ||
'headline': 'headline', | ||
'comment': {} | ||
} | ||
} | ||
} | ||
|
||
perform_error_test(client, data, '/data/attributes/comment/body') | ||
|
||
|
||
def test_third_level_array_error(client, some_blog): | ||
data = { | ||
'data': { | ||
'type': 'entries', | ||
'attributes': { | ||
'blog': some_blog.pk, | ||
'body_text': 'body_text', | ||
'headline': 'headline', | ||
'comments': [ | ||
{ | ||
'body': 'test comment', | ||
'attachments': [ | ||
{ | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} | ||
} | ||
|
||
perform_error_test(client, data, '/data/attributes/comments/0/attachments/0/data') | ||
|
||
|
||
def test_third_level_custom_array_error(client, some_blog): | ||
data = { | ||
'data': { | ||
'type': 'entries', | ||
'attributes': { | ||
'blog': some_blog.pk, | ||
'body_text': 'body_text', | ||
'headline': 'headline', | ||
'comments': [ | ||
{ | ||
'body': 'test comment', | ||
'attachments': [ | ||
{ | ||
'data': 'text' | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} | ||
} | ||
|
||
perform_error_test(client, data, '/data/attributes/comments/0/attachments/0/data') | ||
|
||
|
||
def test_third_level_dict_error(client, some_blog): | ||
data = { | ||
'data': { | ||
'type': 'entries', | ||
'attributes': { | ||
'blog': some_blog.pk, | ||
'body_text': 'body_text', | ||
'headline': 'headline', | ||
'comments': [ | ||
{ | ||
'body': 'test comment', | ||
'attachment': {} | ||
} | ||
] | ||
} | ||
} | ||
} | ||
|
||
perform_error_test(client, data, '/data/attributes/comments/0/attachment/data') | ||
|
||
|
||
def test_many_third_level_dict_errors(client, some_blog): | ||
data = { | ||
'data': { | ||
'type': 'entries', | ||
'attributes': { | ||
'blog': some_blog.pk, | ||
'body_text': 'body_text', | ||
'headline': 'headline', | ||
'comments': [ | ||
{ | ||
'attachment': {} | ||
} | ||
] | ||
} | ||
} | ||
} | ||
|
||
perform_error_test(client, data, '/data/attributes/comments/0/body', 2) | ||
|
||
|
||
@pytest.mark.filterwarning('default::DeprecationWarning:rest_framework_json_api.serializers') | ||
def test_deprecation_warning(recwarn): | ||
class DummyNestedSerializer(serializers.Serializer): | ||
field = serializers.CharField() | ||
|
||
class DummySerializer(serializers.Serializer): | ||
nested = DummyNestedSerializer(many=True) | ||
|
||
assert len(recwarn) == 1 | ||
warning = recwarn.pop(DeprecationWarning) | ||
assert warning | ||
assert str(warning.message).startswith('Rendering') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,13 @@ | ||
import json | ||
|
||
import pytest | ||
from django.test import override_settings | ||
from django.utils import timezone | ||
|
||
from rest_framework_json_api import serializers, views | ||
from rest_framework_json_api.renderers import JSONRenderer | ||
|
||
from example.models import Author, Comment, Entry | ||
from example.models import Author, Comment, Entry, Blog | ||
|
||
|
||
# serializers | ||
|
@@ -38,6 +40,31 @@ class JSONAPIMeta: | |
included_resources = ('related_models',) | ||
|
||
|
||
class EntryDRFSerializers(serializers.ModelSerializer): | ||
|
||
class Meta: | ||
model = Entry | ||
fields = ('headline', 'body_text') | ||
read_only_fields = ('tags',) | ||
|
||
|
||
class CommentWithNestedFieldsSerializer(serializers.ModelSerializer): | ||
entry = EntryDRFSerializers() | ||
|
||
class Meta: | ||
model = Comment | ||
exclude = ('created_at', 'modified_at', 'author') | ||
# fields = ('entry', 'body', 'author',) | ||
|
||
|
||
class AuthorWithNestedFieldsSerializer(serializers.ModelSerializer): | ||
comments = CommentWithNestedFieldsSerializer(many=True) | ||
|
||
class Meta: | ||
model = Author | ||
fields = ('name', 'email', 'comments') | ||
|
||
|
||
# views | ||
class DummyTestViewSet(views.ModelViewSet): | ||
queryset = Entry.objects.all() | ||
|
@@ -49,6 +76,12 @@ class ReadOnlyDummyTestViewSet(views.ReadOnlyModelViewSet): | |
serializer_class = DummyTestSerializer | ||
|
||
|
||
class AuthorWithNestedFieldsViewSet(views.ModelViewSet): | ||
queryset = Author.objects.all() | ||
serializer_class = AuthorWithNestedFieldsSerializer | ||
resource_name = 'authors' | ||
|
||
|
||
def render_dummy_test_serialized_view(view_class, instance): | ||
serializer = view_class.serializer_class(instance=instance) | ||
renderer = JSONRenderer() | ||
|
@@ -138,3 +171,54 @@ def test_extract_relation_instance(comment): | |
field=serializer.fields['blog'], resource_instance=comment | ||
) | ||
assert got == comment.entry.blog | ||
|
||
|
||
def test_attribute_rendering_strategy(db): | ||
# setting up | ||
blog = Blog.objects.create(name='Some Blog', tagline="It's a blog") | ||
entry = Entry.objects.create( | ||
blog=blog, | ||
headline='headline', | ||
body_text='body_text', | ||
pub_date=timezone.now(), | ||
mod_date=timezone.now(), | ||
n_comments=0, | ||
n_pingbacks=0, | ||
rating=3 | ||
) | ||
|
||
author = Author.objects.create(name='some_author', email='[email protected]') | ||
entry.authors.add(author) | ||
|
||
Comment.objects.create( | ||
entry=entry, | ||
body='testing one two three', | ||
author=Author.objects.first() | ||
) | ||
|
||
with override_settings( | ||
JSON_API_SERIALIZE_NESTED_SERIALIZERS_AS_ATTRIBUTE=True): | ||
rendered = render_dummy_test_serialized_view(AuthorWithNestedFieldsViewSet, author) | ||
result = json.loads(rendered.decode()) | ||
|
||
expected = { | ||
"data": { | ||
"type": "authors", | ||
"id": "1", | ||
"attributes": { | ||
"name": "some_author", | ||
"email": "[email protected]", | ||
"comments": [ | ||
{ | ||
"id": 1, | ||
"entry": { | ||
'headline': 'headline', | ||
'body_text': 'body_text', | ||
}, | ||
"body": "testing one two three" | ||
} | ||
] | ||
} | ||
} | ||
} | ||
assert expected == result |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
regex not needed on path()