From b3ca0412f5f8ff00e5999edbcdd07172a48a28cb Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Mon, 3 Aug 2015 19:33:59 +0100 Subject: [PATCH] Merged BaseSerializer with WagtailSerializer Previously, WagtailSerializer and it's subclasses were responsible for serializing objects and it delegated serializing the dynamic fields to automatically generated subclasses of BaseSerializer. Now, these have been merged and all serializers are generated automatically for a particular request. The responsibility of generating the serializers is now in the endpoint. --- wagtail/contrib/wagtailapi/endpoints.py | 42 ++++++++++++-------- wagtail/contrib/wagtailapi/serializers.py | 47 ++++++++++------------- 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/wagtail/contrib/wagtailapi/endpoints.py b/wagtail/contrib/wagtailapi/endpoints.py index b9bf96a9a..1bf3b197c 100644 --- a/wagtail/contrib/wagtailapi/endpoints.py +++ b/wagtail/contrib/wagtailapi/endpoints.py @@ -20,14 +20,14 @@ from .filters import ( ) from .renderers import WagtailJSONRenderer from .pagination import WagtailPagination -from .serializers import WagtailSerializer, PageSerializer, DocumentSerializer +from .serializers import BaseSerializer, PageSerializer, DocumentSerializer, get_serializer_class from .utils import BadRequestError class BaseAPIEndpoint(GenericViewSet): renderer_classes = [WagtailJSONRenderer] pagination_class = WagtailPagination - serializer_class = WagtailSerializer + base_serializer_class = BaseSerializer filter_classes = [] queryset = None # Set on subclasses or implement `get_queryset()`. @@ -87,18 +87,21 @@ class BaseAPIEndpoint(GenericViewSet): if unknown_parameters: raise BadRequestError("query parameter is not an operation or a recognised field: %s" % ', '.join(sorted(unknown_parameters))) - def get_serializer_context(self): - """ - The serialization context differs between listing and detail views. - """ + def get_serializer_class(self): request = self.request + # Get model if self.action == 'listing_view': model = self.get_queryset().model + else: + model = type(self.get_object()) - all_fields = self.get_api_fields(model) - all_fields = list(OrderedDict.fromkeys(all_fields)) # Removes any duplicates in case the developer put "title" in api_fields + # Get all available fields + all_fields = self.get_api_fields(model) + all_fields = list(OrderedDict.fromkeys(all_fields)) # Removes any duplicates in case the developer put "title" in api_fields + if self.action == 'listing_view': + # Listing views just show the title field and any other allowed field the user specified if 'fields' in request.GET: fields = set(request.GET['fields'].split(',')) else: @@ -111,22 +114,27 @@ class BaseAPIEndpoint(GenericViewSet): # Reorder fields so it matches the order of all_fields fields = [field for field in all_fields if field in fields] + else: + # Detail views show all fields all the time + fields = all_fields + return get_serializer_class(model, fields, base=self.base_serializer_class) + + def get_serializer_context(self): + """ + The serialization context differs between listing and detail views. + """ + request = self.request + + if self.action == 'listing_view': return { 'request': request, 'view': self, - 'fields': fields } - model = type(self.get_object()) - - all_fields = self.get_api_fields(model) - all_fields = list(OrderedDict.fromkeys(all_fields)) # Removes any duplicates in case the developer put "title" in api_fields - return { 'request': request, 'view': self, - 'fields': all_fields, 'show_details': True } @@ -155,7 +163,7 @@ class BaseAPIEndpoint(GenericViewSet): class PagesAPIEndpoint(BaseAPIEndpoint): - serializer_class = PageSerializer + base_serializer_class = PageSerializer filter_backends = [ FieldsFilter, ChildOfFilter, @@ -216,7 +224,7 @@ class ImagesAPIEndpoint(BaseAPIEndpoint): class DocumentsAPIEndpoint(BaseAPIEndpoint): queryset = Document.objects.all().order_by('id') - serializer_class = DocumentSerializer + base_serializer_class = DocumentSerializer filter_backends = [FieldsFilter, OrderingFilter, SearchFilter] extra_api_fields = ['title', 'tags'] name = 'documents' diff --git a/wagtail/contrib/wagtailapi/serializers.py b/wagtail/contrib/wagtailapi/serializers.py index 1a07bd525..a67a1dbd0 100644 --- a/wagtail/contrib/wagtailapi/serializers.py +++ b/wagtail/contrib/wagtailapi/serializers.py @@ -14,7 +14,7 @@ from wagtail.utils.compat import get_related_model from wagtail.wagtailcore.models import Page from wagtail.wagtailcore import fields as wagtailcore_fields -from .utils import ObjectDetailURL, URLPath, BadRequestError, pages_for_site +from .utils import ObjectDetailURL, URLPath, pages_for_site class ChildRelationField(Field): @@ -27,7 +27,8 @@ class ChildRelationField(Field): serializer = serializer_class() return [ - serializer.to_representation(child_object) + # Use rest frameworks to_representation method so we don't add id/meta attributes + super(BaseSerializer, serializer).to_representation(child_object) for child_object in value.all() ] @@ -88,24 +89,8 @@ class BaseSerializer(serializers.ModelSerializer): return super(BaseSerializer, self).build_relational_field(field_name, relation_info) - -def get_serializer_class(model_, fields_): - class Meta: - model = model_ - fields = fields_ - - return type(model_.__name__ + 'Serializer', (BaseSerializer, ), { - 'Meta': Meta - }) - - -class WagtailSerializer(serializers.BaseSerializer): def to_representation(self, instance): - fields = self.context.get('fields') - return self.serialize_object( - instance, - fields=fields, - ) + return self.serialize_object(instance) def serialize_object_metadata(self, obj): """ @@ -120,7 +105,7 @@ class WagtailSerializer(serializers.BaseSerializer): return data - def serialize_object(self, obj, fields, extra_data=()): + def serialize_object(self, obj, extra_data=()): """ This converts an object into JSON-serialisable dict so it can be used in the API. @@ -138,14 +123,12 @@ class WagtailSerializer(serializers.BaseSerializer): data.extend(extra_data) # Serialize the fields - serializer_class = get_serializer_class(type(obj), fields) - serializer = serializer_class() - data.extend(serializer.to_representation(obj).items()) + data.extend(super(BaseSerializer, self).to_representation(obj).items()) return OrderedDict(data) -class PageSerializer(WagtailSerializer): +class PageSerializer(BaseSerializer): def serialize_object_metadata(self, page): data = super(PageSerializer, self).serialize_object_metadata(page) @@ -154,7 +137,7 @@ class PageSerializer(WagtailSerializer): return data - def serialize_object(self, page, fields, extra_data=()): + def serialize_object(self, page, extra_data=()): # Add parent if self.context.get('show_details', False): parent = page.get_parent() @@ -173,10 +156,10 @@ class PageSerializer(WagtailSerializer): ])), ) - return super(PageSerializer, self).serialize_object(page, fields, extra_data=extra_data) + return super(PageSerializer, self).serialize_object(page, extra_data=extra_data) -class DocumentSerializer(WagtailSerializer): +class DocumentSerializer(BaseSerializer): def serialize_object_metadata(self, document): data = super(DocumentSerializer, self).serialize_object_metadata(document) @@ -185,3 +168,13 @@ class DocumentSerializer(WagtailSerializer): data['download_url'] = URLPath(document.url) return data + + +def get_serializer_class(model_, fields_, base=BaseSerializer): + class Meta: + model = model_ + fields = fields_ + + return type(model_.__name__ + 'Serializer', (base, ), { + 'Meta': Meta + })