Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
35 changes: 0 additions & 35 deletions rest_framework_json_api/relations.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import json

from rest_framework.exceptions import ValidationError
from rest_framework.fields import MISSING_ERROR_MESSAGE
from rest_framework.relations import *
from django.utils.translation import ugettext_lazy as _
Expand All @@ -10,40 +9,6 @@
get_resource_type_from_queryset, get_resource_type_from_instance


class HyperlinkedRelatedField(HyperlinkedRelatedField):
"""
This field exists for the sole purpose of accepting PKs as well as URLs
when data is submitted back to the serializer
"""
default_error_messages = {
'required': _('This field is required.'),
'no_match': _('Invalid hyperlink - No URL match.'),
'incorrect_match': _('Invalid hyperlink - Incorrect URL match.'),
'does_not_exist': _('Invalid hyperlink - Object does not exist.'),
'incorrect_type': _('Incorrect type. Expected URL string, received {data_type}.'),
'pk_does_not_exist': _('Invalid pk "{pk_value}" - object does not exist.'),
'incorrect_pk_type': _('Incorrect type. Expected pk value, received {data_type}.'),
}

def __init__(self, **kwargs):
self.pk_field = kwargs.pop('pk_field', None)
super(HyperlinkedRelatedField, self).__init__(**kwargs)

def to_internal_value(self, data):
try:
# Try parsing links first for the browseable API
return super(HyperlinkedRelatedField, self).to_internal_value(data)
except ValidationError:
if self.pk_field is not None:
data = self.pk_field.to_internal_value(data)
try:
return self.get_queryset().get(pk=data)
except ObjectDoesNotExist:
self.fail('pk_does_not_exist', pk_value=data)
except (TypeError, ValueError):
self.fail('incorrect_pk_type', data_type=type(data).__name__)


class ResourceRelatedField(PrimaryKeyRelatedField):
self_link_view_name = None
related_link_view_name = None
Expand Down
50 changes: 36 additions & 14 deletions rest_framework_json_api/serializers.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework.serializers import *
from rest_framework_json_api.utils import format_relation_name, get_resource_type_from_instance

from rest_framework_json_api.relations import HyperlinkedRelatedField


class HyperlinkedModelSerializer(HyperlinkedModelSerializer):
"""
A type of `ModelSerializer` that uses hyperlinked relationships instead
of primary key relationships. Specifically:

* A 'url' field is included instead of the 'id' field.
* Relationships to other instances are hyperlinks, instead of primary keys.
* Uses django-rest-framework-json-api HyperlinkedRelatedField instead of the default HyperlinkedRelatedField
"""
serializer_related_field = HyperlinkedRelatedField
from rest_framework_json_api.utils import format_relation_name, get_resource_type_from_instance, \
get_resource_type_from_serializer


class ResourceIdentifierObjectSerializer(BaseSerializer):
Expand Down Expand Up @@ -46,3 +34,37 @@ def to_internal_value(self, data):
self.fail('does_not_exist', pk_value=pk)
except (TypeError, ValueError):
self.fail('incorrect_type', data_type=type(data['pk']).__name__)


class SparseFieldsetsMixin(object):
def __init__(self, *args, **kwargs):
context = kwargs.get('context')
request = context.get('request') if context else None

if request:
sparse_fieldset_query_param = 'fields[{}]'.format(get_resource_type_from_serializer(self))
try:
param_name = next(key for key in request.query_params if sparse_fieldset_query_param in key)
except StopIteration:
pass
else:
fieldset = request.query_params.get(param_name).split(',')
for field_name, field in self.fields.items():
if field_name == api_settings.URL_FIELD_NAME: # leave self link there
continue
if field_name not in fieldset:
self.fields.pop(field_name)

super(SparseFieldsetsMixin, self).__init__(*args, **kwargs)


class HyperlinkedModelSerializer(SparseFieldsetsMixin, HyperlinkedModelSerializer):
"""
A type of `ModelSerializer` that uses hyperlinked relationships instead
of primary key relationships. Specifically:

* A 'url' field is included instead of the 'id' field.
* Relationships to other instances are hyperlinks, instead of primary keys.

* A mixin class to enable sparse fieldsets is included
"""
2 changes: 1 addition & 1 deletion rest_framework_json_api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def get_resource_type_from_serializer(serializer):
# Check the meta class for resource_name
return serializer.Meta.resource_name
except AttributeError:
# Use the serializer model then then pluralize and format
# Use the serializer model then pluralize and format
return format_relation_name(serializer.Meta.model.__name__)


Expand Down