Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:django-json-api/django-rest-fram…
Browse files Browse the repository at this point in the history
…ework-json-api
  • Loading branch information
jerel committed Sep 26, 2016
2 parents 3471569 + cebecf5 commit 9aa259c
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 25 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var/
*.egg-info/
.installed.cfg
*.egg
.eggs/

# Installer logs
pip-log.txt
Expand Down
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ env:

- DJANGO=">=1.10,<1.11" DRF=">=3.4,<3.5"
before_install:
# Force an upgrade of py to avoid VersionConflict
# Force an upgrade of py & pytest to avoid VersionConflict
- pip install --upgrade py
- pip install "pytest>=2.8,<3"
- pip install codecov
install:
- pip install Django${DJANGO} djangorestframework${DRF}
Expand Down
4 changes: 2 additions & 2 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ class OrderSerializer(serializers.ModelSerializer):

customer = ResourceRelatedField(
queryset=Customer.objects,
related_link_view-name='order-customer-detail',
related_link_view_name='order-customer-detail',
related_link_url_kwarg='order_pk',
self_link_view_name='order-relationships'
)
Expand Down Expand Up @@ -399,7 +399,7 @@ The urlconf would need to contain a route like the following:

```python
url(
regex=r'^orders/(?P<pk>[^/.]+/relationships/(?P<related_field>[^/.]+)$',
regex=r'^orders/(?P<pk>[^/.]+)/relationships/(?P<related_field>[^/.]+)$',
view=OrderRelationshipView.as_view(),
name='order-relationships'
)
Expand Down
31 changes: 31 additions & 0 deletions example/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""
Test rest_framework_json_api's utils functions.
"""
from rest_framework_json_api import utils

from ..serializers import EntrySerializer
from ..tests import TestBase


class GetRelatedResourceTests(TestBase):
"""
Ensure the `get_related_resource_type` function returns correct types.
"""

def test_reverse_relation(self):
"""
Ensure reverse foreign keys have their types identified correctly.
"""
serializer = EntrySerializer()
field = serializer.fields['comments']

self.assertEqual(utils.get_related_resource_type(field), 'comments')

def test_m2m_relation(self):
"""
Ensure m2ms have their types identified correctly.
"""
serializer = EntrySerializer()
field = serializer.fields['authors']

self.assertEqual(utils.get_related_resource_type(field), 'authors')
45 changes: 45 additions & 0 deletions example/tests/unit/test_renderers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from example.models import Entry, Comment
from rest_framework_json_api import serializers, views
from rest_framework_json_api.renderers import JSONRenderer


# serializers
class RelatedModelSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ('id',)


class DummyTestSerializer(serializers.ModelSerializer):
'''
This serializer is a simple compound document serializer which includes only
a single embedded relation
'''
related_models = RelatedModelSerializer(
source='comment_set', many=True, read_only=True)

class Meta:
model = Entry
fields = ('related_models',)

class JSONAPIMeta:
included_resources = ('related_models',)


# views
class DummyTestViewSet(views.ModelViewSet):
queryset = Entry.objects.all()
serializer_class = DummyTestSerializer


def test_simple_reverse_relation_included_renderer():
'''
Test renderer when a single reverse fk relation is passed.
'''
serializer = DummyTestSerializer(instance=Entry())
renderer = JSONRenderer()
rendered = renderer.render(
serializer.data,
renderer_context={'view': DummyTestViewSet()})

assert rendered
2 changes: 1 addition & 1 deletion requirements-development.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-e .
pytest==2.8.2
pytest>=2.9.0,<3.0
pytest-django
pytest-factoryboy
fake-factory
Expand Down
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.1.0'
__version__ = '2.1.1'
__author__ = ''
__license__ = 'MIT'
__copyright__ = ''
Expand Down
2 changes: 1 addition & 1 deletion rest_framework_json_api/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def parse(self, stream, media_type=None, parser_context=None):
raise ParseError("The resource identifier object must contain an 'id' member")

# Construct the return data
parsed_data = {'id': data.get('id')}
parsed_data = {'id': data.get('id')} if 'id' in data else {}
parsed_data.update(self.parse_attributes(data))
parsed_data.update(self.parse_relationships(data))
parsed_data.update(self.parse_metadata(result))
Expand Down
2 changes: 1 addition & 1 deletion rest_framework_json_api/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def extract_relationships(fields, resource, resource_instance):
})
continue

if isinstance(field, ListSerializer) and relation_instance is not None:
if isinstance(field, ListSerializer):
resolved, relation_instance = utils.get_relation_instance(resource_instance, source, field.parent)
if not resolved:
continue
Expand Down
44 changes: 27 additions & 17 deletions rest_framework_json_api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@
Utils.
"""
import copy
import inspect
import warnings
from collections import OrderedDict
import inspect

import inflection
from rest_framework import exceptions
from rest_framework.exceptions import APIException

import django
from django.conf import settings
from django.utils import encoding
from django.utils import six
from django.db.models import Manager
from django.utils import encoding, six
from django.utils.module_loading import import_string as import_class_from_dotted_path
from django.utils.translation import ugettext_lazy as _
from django.db.models import Manager
from rest_framework.exceptions import APIException
from rest_framework import exceptions

try:
from rest_framework.serializers import ManyRelatedField
Expand All @@ -26,6 +27,14 @@
except ImportError:
HyperlinkedRouterField = type(None)

if django.VERSION >= (1, 9):
from django.db.models.fields.related_descriptors import ManyToManyDescriptor, ReverseManyToOneDescriptor
ReverseManyRelatedObjectsDescriptor = type(None)
else:
from django.db.models.fields.related import ManyRelatedObjectsDescriptor as ManyToManyDescriptor
from django.db.models.fields.related import ForeignRelatedObjectsDescriptor as ReverseManyToOneDescriptor
from django.db.models.fields.related import ReverseManyRelatedObjectsDescriptor


def get_resource_name(context):
"""
Expand Down Expand Up @@ -87,6 +96,7 @@ def get_serializer_fields(serializer):
pass
return fields


def format_keys(obj, format_type=None):
"""
Takes either a dict or list and returns it with camelized keys only if
Expand Down Expand Up @@ -148,6 +158,7 @@ def format_relation_name(value, format_type=None):
pluralize = getattr(settings, 'JSON_API_PLURALIZE_RELATION_TYPE', None)
return format_resource_type(value, format_type, pluralize)


def format_resource_type(value, format_type=None, pluralize=None):
if format_type is None:
format_type = getattr(settings, 'JSON_API_FORMAT_TYPES', False)
Expand All @@ -167,7 +178,6 @@ def get_related_resource_type(relation):
return get_resource_type_from_serializer(relation)
except AttributeError:
pass

relation_model = None
if hasattr(relation, '_meta'):
relation_model = relation._meta.model
Expand All @@ -184,7 +194,7 @@ def get_related_resource_type(relation):
elif hasattr(parent_serializer, 'parent') and hasattr(parent_serializer.parent, 'Meta'):
parent_model = getattr(parent_serializer.parent.Meta, 'model', None)

if parent_model is not None:
if parent_model is not None:
if relation.source:
if relation.source != '*':
parent_model_relation = getattr(parent_model, relation.source)
Expand All @@ -193,17 +203,17 @@ def get_related_resource_type(relation):
else:
parent_model_relation = getattr(parent_model, parent_serializer.field_name)

if hasattr(parent_model_relation, 'related'):
try:
if type(parent_model_relation) is ReverseManyToOneDescriptor:
if django.VERSION >= (1, 9):
relation_model = parent_model_relation.rel.related_model
elif django.VERSION >= (1, 8):
relation_model = parent_model_relation.related.related_model
except AttributeError:
# Django 1.7
else:
relation_model = parent_model_relation.related.model
elif hasattr(parent_model_relation, 'field'):
try:
relation_model = parent_model_relation.field.remote_field.model
except AttributeError:
relation_model = parent_model_relation.field.related.model
elif type(parent_model_relation) is ManyToManyDescriptor:
relation_model = parent_model_relation.field.remote_field.model
elif type(parent_model_relation) is ReverseManyRelatedObjectsDescriptor:
relation_model = parent_model_relation.field.related.model
else:
return get_related_resource_type(parent_model_relation)

Expand Down
14 changes: 14 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,17 @@ test = pytest

[wheel]
universal = 1

[flake8]
ignore = E501
max-line-length = 100

[isort]
known_django = django
sections = FUTURE,STDLIB,THIRDPARTY,DJANGO,FIRSTPARTY,LOCALFOLDER
default_section = THIRDPARTY
known_standard_library = factory,mock,requests
known_first_party = rest_framework_json_api
multi_line_output = 3
line_length = 100
indent = 4
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def get_package_data(package):
tests_require=[
'pytest-factoryboy',
'pytest-django',
'pytest>=2.8',
'pytest>=2.8,<3',
] + mock,
zip_safe=False,
)

0 comments on commit 9aa259c

Please sign in to comment.