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 + })