Skip to content
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

HAL List Serializer #20

Merged
merged 10 commits into from
Sep 26, 2017
40 changes: 39 additions & 1 deletion drf_hal_json/serializers.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,64 @@
from collections import defaultdict

from rest_framework.utils.serializer_helpers import ReturnDict

from drf_hal_json import EMBEDDED_FIELD_NAME, LINKS_FIELD_NAME, URL_FIELD_NAME
from drf_hal_json.fields import HalHyperlinkedPropertyField, HalContributeToLinkField, \
HalHyperlinkedSerializerMethodField
from rest_framework.fields import empty, FileField, ImageField
from rest_framework.relations import HyperlinkedIdentityField, HyperlinkedRelatedField, ManyRelatedField, RelatedField
from rest_framework.serializers import BaseSerializer, HyperlinkedModelSerializer
from rest_framework.serializers import BaseSerializer, HyperlinkedModelSerializer, ListSerializer
from rest_framework.utils.field_mapping import get_nested_relation_kwargs


class HalListSerializer(ListSerializer):

@property
def data(self):
# The parent class returns ReturnList
return ReturnDict(
{
LINKS_FIELD_NAME: {
URL_FIELD_NAME: {
'href': self.context['request'].build_absolute_uri()
}
},
EMBEDDED_FIELD_NAME: {
# `items` mirrors hardcoded value in pagination classes
'items': super(ListSerializer, self).data
}
},
serializer=self
)


class HalModelSerializer(HyperlinkedModelSerializer):
"""
Serializer for HAL representation of django models
"""
serializer_related_field = HyperlinkedRelatedField
default_list_serializer = HalListSerializer

def __init__(self, instance=None, data=empty, **kwargs):
super(HalModelSerializer, self).__init__(instance, data, **kwargs)
self.nested_serializer_class = self.__class__
if data != empty and not LINKS_FIELD_NAME in data:
data[LINKS_FIELD_NAME] = dict() # put links in data, so that field validation does not fail

@classmethod
def many_init(cls, *args, **kwargs):
# inject the default into list_serializer_class (if not present)
meta = getattr(cls, 'Meta', None)
if meta is None:
class Meta:
pass
meta = Meta
setattr(cls, 'Meta', meta)
list_serializer_class = getattr(meta, 'list_serializer_class', None)
if list_serializer_class is None:
setattr(meta, 'list_serializer_class', cls.default_list_serializer)
return super(HalModelSerializer, cls).many_init(*args, **kwargs)

def build_link_object(self, val):
if (type([]) == type(val)):
return [self.build_link_object(v) for v in val]
Expand Down
9 changes: 9 additions & 0 deletions tests/testproject/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ def test_pagination(self):
self.assertIn("next", pages["_links"])
next_link = pages["_links"]["next"]["href"]

unpaged = self.client.get("/abundant-unpaged/").data
# Renders with _links and _embedded
self.assertIn("_links", unpaged)
self.assertIn("_embedded", unpaged)
self.assertIn("self", unpaged["_links"])
# Includes no pages
self.assertNotIn("previous", unpaged["_links"])
self.assertNotIn("next", unpaged["_links"])

pages = self.client.get(next_link).data
self.assertIn("self", pages["_links"])
self.assertIn("previous", pages["_links"])
Expand Down
3 changes: 2 additions & 1 deletion tests/testproject/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .views import (AbundantResourceViewSet, CustomResourceViewSet,
RelatedResource1ViewSet, RelatedResource2ViewSet,
RelatedResource3ViewSet, TestResourceViewSet,
URLResourceViewSet, FileResourceViewSet)
URLResourceViewSet, FileResourceViewSet, AbundantUnpagedViewSet)

router = DefaultRouter()
router.register(r'test-resources', TestResourceViewSet)
Expand All @@ -13,6 +13,7 @@
router.register(r'related-resources-3', RelatedResource3ViewSet)
router.register(r'custom-resources', CustomResourceViewSet)
router.register(r'abundant-resources', AbundantResourceViewSet)
router.register(r'abundant-unpaged', AbundantUnpagedViewSet)
router.register(r'url-resources', URLResourceViewSet)
router.register(r'file-resources', FileResourceViewSet)
urlpatterns = router.urls
4 changes: 4 additions & 0 deletions tests/testproject/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class AbundantResourceViewSet(HalCreateModelMixin, ModelViewSet):
queryset = AbundantResource.objects.all()


class AbundantUnpagedViewSet(AbundantResourceViewSet):
pagination_class = None


class URLResourceViewSet(HalCreateModelMixin, ModelViewSet):
serializer_class = HyperlinkedPropertySerializer
queryset = URLResource.objects.all()
Expand Down