Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
66c1eef
Use serializer `resource_name` for nested objects when present
jsenecal Oct 16, 2015
33ffa01
Use serializer `resource_name` for nested objects in relationships too
jsenecal Oct 19, 2015
73bc6d5
Use serializer `resource_name` for nested ModelSerializers
jsenecal Oct 19, 2015
42e0554
Merge branch develop into feature/compound_documents
jerel Dec 8, 2015
52a5b66
added a get_resource_type_from_model
scottfisk Nov 5, 2015
e783a43
dropped format_relation_name from wrapping get_resource_type*
scottfisk Nov 5, 2015
e35353e
Added basic test for model resource_name property
scottfisk Dec 1, 2015
e329145
started testing all combinations of resource_name on model, serialize…
Dec 11, 2015
867e209
remove proxy=True on model
Dec 11, 2015
fea279d
leftover uses of model.__name__
scottfisk Dec 16, 2015
fe0056d
use an included serializer's resource_name even when not included
scottfisk Dec 16, 2015
736837f
cleaned up tests
scottfisk Dec 16, 2015
a8ee085
support many=True fields that are not Model relationships (calculated…
johnhuffsmith Dec 16, 2015
bd5a955
added view precedence test
scottfisk Dec 16, 2015
01a5417
Merge remote-tracking branch 'upstream/develop' into develop
johnhuffsmith Dec 21, 2015
d47a7da
relation_type is jacking up serialization on fields where its not needed
johnhuffsmith Dec 21, 2015
836391f
Use py.test client over drf client to fix Django 1.7 tests
scottfisk Dec 24, 2015
325f3b9
Merge pull request #152 from scottfisk/feature/compound_documents
jsenecal Dec 25, 2015
acca465
Merge branch 'develop' of https://github.com/django-json-api/django-r…
johnhuffsmith Dec 28, 2015
a343f71
mergin feature/compound_documents
johnhuffsmith Dec 28, 2015
8672308
random tweaks
johnhuffsmith Dec 28, 2015
9f0b0fc
saving
johnhuffsmith Jan 2, 2016
dc97bf0
cleanup
johnhuffsmith Jan 4, 2016
0b94142
cleanup
johnhuffsmith Jan 6, 2016
7d058a4
preventing explosion on skipped/empty fields
johnhuffsmith Jan 25, 2016
a0d3986
better fix
johnhuffsmith Jan 25, 2016
0dcafd3
Merge pull request #1 from Mariana-Tek/skippedfieldfix
johnhuffsmith Jan 25, 2016
570e41c
fixing type
johnhuffsmith Jan 26, 2016
4161ad4
Merge pull request #2 from Mariana-Tek/includedserializerfix
johnhuffsmith Jan 26, 2016
6ef3596
changed 'id' to 'pk' in MultipleIDMixin to be compatible w/ Ember
akubetin Jan 26, 2016
740aa48
Merge branch 'master' of https://github.com/Mariana-Tek/django-rest-f…
akubetin Jan 26, 2016
2069237
Fix potential circular import edge-case
asteinlein Feb 6, 2016
edf2226
Do not assume multiple queryset evals return in same order
Mar 21, 2016
be69de2
Merge pull request #4 from Mariana-Tek/bug/queryset-no-order-by-mismatch
grjones Mar 22, 2016
a1c1769
Use source field of 'id' field in lookup
Mar 22, 2016
4c728bd
fix extract_included for ManyRelatedField
cathrynsupko Apr 18, 2016
dffbbe3
Merge pull request #5 from Mariana-Tek/extract_included
cathrynsupko Apr 18, 2016
793a07b
remove redundant deduping
cathrynsupko Apr 18, 2016
2f91ed9
Merge pull request #6 from Mariana-Tek/extract_included
cathrynsupko Apr 18, 2016
abbc805
changing logic in rest_framework_json_api.parsers.JSONParser.parse_re…
akubetin Apr 20, 2016
da44a92
small change to accomodate empty dictionaries without giving 500 errors
akubetin Apr 21, 2016
4eabb3c
Default serializer can be set via method
Apr 26, 2016
563a4da
Merge pull request #7 from Mariana-Tek/default-serializer-by-method
grjones Apr 26, 2016
911d34b
use drf get_attribute to support dot notation source lookups
johnhuffsmith Jun 20, 2016
84897c7
Merge pull request #8 from Mariana-Tek/get-attribute
johnhuffsmith Jun 20, 2016
9588b0e
proper source usage
johnhuffsmith Jun 23, 2016
98e7595
Merge pull request #9 from Mariana-Tek/bug-#121957675
johnhuffsmith Jun 24, 2016
c810fd1
use drf get_attribute to support dot notation source lookups
johnhuffsmith Jun 20, 2016
7af3fa1
proper source usage
johnhuffsmith Jun 23, 2016
a34eac8
passing type into serializers
johnhuffsmith Jul 29, 2016
49b6011
bumping version
johnhuffsmith Jul 29, 2016
b11c86f
Merge pull request #10 from Mariana-Tek/pass_type_context
johnhuffsmith Aug 3, 2016
8b32da5
skip lookup if None
johnhuffsmith Aug 7, 2016
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
3 changes: 1 addition & 2 deletions example/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ def get_body_format(self, obj):
class Meta:
model = Entry
fields = ('blog', 'headline', 'body_text', 'pub_date', 'mod_date',
'authors', 'comments', 'suggested',)
meta_fields = ('body_format',)
'authors', 'comments', 'suggested',)


class AuthorBioSerializer(serializers.ModelSerializer):
Expand Down
137 changes: 137 additions & 0 deletions example/tests/integration/test_model_resource_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import pytest
from django.core.urlresolvers import reverse

from example.tests.utils import load_json

from example import models, serializers, views
pytestmark = pytest.mark.django_db


class _PatchedModel:
class JSONAPIMeta:
resource_name = "resource_name_from_JSONAPIMeta"


def _check_resource_and_relationship_comment_type_match(django_client):
entry_response = django_client.get(reverse("entry-list"))
comment_response = django_client.get(reverse("comment-list"))

comment_resource_type = load_json(comment_response.content).get('data')[0].get('type')
comment_relationship_type = load_json(entry_response.content).get(
'data')[0].get('relationships').get('comments').get('data')[0].get('type')

assert comment_resource_type == comment_relationship_type, "The resource type seen in the relationships and head resource do not match"


def _check_relationship_and_included_comment_type_are_the_same(django_client, url):
response = django_client.get(url + "?include=comments")
data = load_json(response.content).get('data')[0]
comment = load_json(response.content).get('included')[0]

comment_relationship_type = data.get('relationships').get('comments').get('data')[0].get('type')
comment_included_type = comment.get('type')

assert comment_relationship_type == comment_included_type, "The resource type seen in the relationships and included do not match"


@pytest.mark.usefixtures("single_entry")
class TestModelResourceName:

def test_model_resource_name_on_list(self, client):
models.Comment.__bases__ += (_PatchedModel,)
response = client.get(reverse("comment-list"))
data = load_json(response.content)['data'][0]
# name should be super-author instead of model name RenamedAuthor
assert (data.get('type') == 'resource_name_from_JSONAPIMeta'), (
'resource_name from model incorrect on list')

# Precedence tests
def test_resource_name_precendence(self, client):
# default
response = client.get(reverse("comment-list"))
data = load_json(response.content)['data'][0]
assert (data.get('type') == 'comments'), (
'resource_name from model incorrect on list')

# model > default
models.Comment.__bases__ += (_PatchedModel,)
response = client.get(reverse("comment-list"))
data = load_json(response.content)['data'][0]
assert (data.get('type') == 'resource_name_from_JSONAPIMeta'), (
'resource_name from model incorrect on list')

# serializer > model
serializers.CommentSerializer.Meta.resource_name = "resource_name_from_serializer"
response = client.get(reverse("comment-list"))
data = load_json(response.content)['data'][0]
assert (data.get('type') == 'resource_name_from_serializer'), (
'resource_name from serializer incorrect on list')

# view > serializer > model
views.CommentViewSet.resource_name = 'resource_name_from_view'
response = client.get(reverse("comment-list"))
data = load_json(response.content)['data'][0]
assert (data.get('type') == 'resource_name_from_view'), (
'resource_name from view incorrect on list')

def teardown_method(self, method):
models.Comment.__bases__ = (models.Comment.__bases__[0],)
try:
delattr(serializers.CommentSerializer.Meta, "resource_name")
except AttributeError:
pass
try:
delattr(views.CommentViewSet, "resource_name")
except AttributeError:
pass


@pytest.mark.usefixtures("single_entry")
class TestResourceNameConsistency:

# Included rename tests
def test_type_match_on_included_and_inline_base(self, client):
_check_relationship_and_included_comment_type_are_the_same(client, reverse("entry-list"))

def test_type_match_on_included_and_inline_with_JSONAPIMeta(self, client):
models.Comment.__bases__ += (_PatchedModel,)

_check_relationship_and_included_comment_type_are_the_same(client, reverse("entry-list"))

def test_type_match_on_included_and_inline_with_serializer_resource_name(self, client):
serializers.CommentSerializer.Meta.resource_name = "resource_name_from_serializer"

_check_relationship_and_included_comment_type_are_the_same(client, reverse("entry-list"))

def test_type_match_on_included_and_inline_with_serializer_resource_name_and_JSONAPIMeta(self, client):
models.Comment.__bases__ += (_PatchedModel,)
serializers.CommentSerializer.Meta.resource_name = "resource_name_from_serializer"

_check_relationship_and_included_comment_type_are_the_same(client, reverse("entry-list"))

# Relation rename tests
def test_resource_and_relationship_type_match(self, client):
_check_resource_and_relationship_comment_type_match(client)

def test_resource_and_relationship_type_match_with_serializer_resource_name(self, client):
serializers.CommentSerializer.Meta.resource_name = "resource_name_from_serializer"

_check_resource_and_relationship_comment_type_match(client)

def test_resource_and_relationship_type_match_with_JSONAPIMeta(self, client):
models.Comment.__bases__ += (_PatchedModel,)

_check_resource_and_relationship_comment_type_match(client)

def test_resource_and_relationship_type_match_with_serializer_resource_name_and_JSONAPIMeta(self, client):
models.Comment.__bases__ += (_PatchedModel,)
serializers.CommentSerializer.Meta.resource_name = "resource_name_from_serializer"

_check_resource_and_relationship_comment_type_match(client)

def teardown_method(self, method):
models.Comment.__bases__ = (models.Comment.__bases__[0],)
try:
delattr(serializers.CommentSerializer.Meta, "resource_name")
except AttributeError:
pass
3 changes: 2 additions & 1 deletion example/urls_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
from rest_framework import routers

from example.views import BlogViewSet, EntryViewSet, AuthorViewSet, EntryRelationshipView, BlogRelationshipView, \
CommentRelationshipView, AuthorRelationshipView
CommentRelationshipView, AuthorRelationshipView, CommentViewSet
from .api.resources.identity import Identity, GenericIdentity

router = routers.DefaultRouter(trailing_slash=False)

router.register(r'blogs', BlogViewSet)
router.register(r'entries', EntryViewSet)
router.register(r'authors', AuthorViewSet)
router.register(r'comments', CommentViewSet)

# for the old tests
router.register(r'identities', Identity)
Expand Down
1 change: 0 additions & 1 deletion example/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,3 @@ class CommentRelationshipView(RelationshipView):
class AuthorRelationshipView(RelationshipView):
queryset = Author.objects.all()
self_link_view_name = 'author-relationships'

2 changes: 1 addition & 1 deletion rest_framework_json_api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-

__title__ = 'djangorestframework-jsonapi'
__version__ = '2.0.0-alpha.3'
__version__ = '3.0.0-mt'
__author__ = ''
__license__ = 'MIT'
__copyright__ = ''
Expand Down
8 changes: 7 additions & 1 deletion rest_framework_json_api/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
from django.utils import six, encoding
from django.utils.translation import ugettext_lazy as _
from rest_framework import status, exceptions
from rest_framework.views import exception_handler as drf_exception_handler

from rest_framework_json_api.utils import format_value


def exception_handler(exc, context):
# Import this here to avoid potential edge-case circular imports, which
# crashes with:
# "ImportError: Could not import 'rest_framework_json_api.parsers.JSONParser' for API setting
# 'DEFAULT_PARSER_CLASSES'. ImportError: cannot import name 'exceptions'.'"
#
# Also see: https://github.com/django-json-api/django-rest-framework-json-api/issues/158
from rest_framework.views import exception_handler as drf_exception_handler
response = drf_exception_handler(exc, context)

if not response:
Expand Down
1 change: 1 addition & 0 deletions rest_framework_json_api/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def get_field_info(self, field):
for choice_value, choice_name in field.choices.items()
]

#Allow includes for all Relations unless explicitly
if hasattr(serializer, 'included_serializers') and 'relationship_resource' in field_info:
field_info['allows_include'] = field.field_name in serializer.included_serializers

Expand Down
7 changes: 6 additions & 1 deletion rest_framework_json_api/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ def get_queryset(self):
ids = dict(self.request.query_params).get('ids[]')
else:
ids = dict(self.request.QUERY_PARAMS).get('ids[]')

if 'filter[id]' in self.request.query_params:
ids = self.request.query_params.get('filter[id]').split(',')

if ids:
self.queryset = self.queryset.filter(id__in=ids)
self.queryset = self.queryset.filter(pk__in=ids)

return self.queryset

67 changes: 38 additions & 29 deletions rest_framework_json_api/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,53 +39,62 @@ def parse_relationships(data):
# Parse the relationships
parsed_relationships = dict()
for field_name, field_data in relationships.items():
field_data = field_data.get('data')
field_data = field_data.get('data', None)
if isinstance(field_data, dict):
parsed_relationships[field_name] = field_data
elif isinstance(field_data, list):
parsed_relationships[field_name] = list(relation for relation in field_data)
elif field_data == None:
parsed_relationships[field_name] = field_data
return parsed_relationships

def parse(self, stream, media_type=None, parser_context=None):
"""
Parses the incoming bytestream as JSON and returns the resulting data
"""
result = super(JSONParser, self).parse(stream, media_type=media_type, parser_context=parser_context)

data = result.get('data')

if data:
from rest_framework_json_api.views import RelationshipView
if isinstance(parser_context['view'], RelationshipView):
# We skip parsing the object as JSONAPI Resource Identifier Object and not a regular Resource Object
if isinstance(data, list):
for resource_identifier_object in data:
if not (resource_identifier_object.get('id') and resource_identifier_object.get('type')):
raise ParseError(
'Received data contains one or more malformed JSONAPI Resource Identifier Object(s)'
)
elif not (data.get('id') and data.get('type')):
raise ParseError('Received data is not a valid JSONAPI Resource Identifier Object')
type = data.get('type')
# if type is defined, treat it like a full object, otherwise just pass through the data
if type:
from rest_framework_json_api.views import RelationshipView
if isinstance(parser_context['view'], RelationshipView):
# We skip parsing the object as JSONAPI Resource Identifier Object and not a regular Resource Object
if isinstance(data, list):
for resource_identifier_object in data:
if not (resource_identifier_object.get('id') and resource_identifier_object.get('type')):
raise ParseError(
'Received data contains one or more malformed JSONAPI Resource Identifier Object(s)'
)
elif not (data.get('id') and data.get('type')):
raise ParseError('Received data is not a valid JSONAPI Resource Identifier Object')

return data
return data

request = parser_context.get('request')
request = parser_context.get('request')

# Check for inconsistencies
resource_name = utils.get_resource_name(parser_context)
if data.get('type') != resource_name and request.method in ('PUT', 'POST', 'PATCH'):
raise exceptions.Conflict(
"The resource object's type ({data_type}) is not the type "
"that constitute the collection represented by the endpoint ({resource_type}).".format(
data_type=data.get('type'),
resource_type=resource_name
# Check for inconsistencies
resource_name = utils.get_resource_name(parser_context)
if data.get('type') != resource_name and request.method in ('PUT', 'POST', 'PATCH'):
raise exceptions.Conflict(
"The resource object's type ({data_type}) is not the type "
"that constitute the collection represented by the endpoint ({resource_type}).".format(
data_type=data.get('type'),
resource_type=resource_name
)
)
)

# Construct the return data
parsed_data = {'id': data.get('id')}
parsed_data.update(self.parse_attributes(data))
parsed_data.update(self.parse_relationships(data))
return parsed_data
# Construct the return data
parsed_data = {'id': data.get('id')}
parsed_data.update(self.parse_attributes(data))
parsed_data.update(self.parse_relationships(data))
return parsed_data

else:
return data

else:
raise ParseError('Received document does not contain primary data')
return {}
Loading