This commit is contained in:
Uriel Corfa 2014-05-28 02:44:38 +00:00
commit d0b3bb4d6a
3 changed files with 70 additions and 8 deletions

View File

@ -64,7 +64,8 @@ Each of the concrete generic views provided is built by combining `GenericAPIVie
The following attributes control the basic view behavior. The following attributes control the basic view behavior.
* `queryset` - The queryset that should be used for returning objects from this view. Typically, you must either set this attribute, or override the `get_queryset()` method. * `queryset` - The queryset that should be used for returning objects from this view. Typically, you must either set this attribute, or override the `get_queryset()` method.
* `serializer_class` - The serializer class that should be used for validating and deserializing input, and for serializing output. Typically, you must either set this attribute, or override the `get_serializer_class()` method. * `serializer_class` - The serializer class that should be used for validating and deserializing input. Typically, you must either set this attribute, or override the `get_serializer_class()` method.
* `serializer_class_for_output` - The serializer class that should be used for deserializing input, and for serializing output. You don't have to set this, if it is unset, `serializer_class` is used instead by default. You can override `get_serializer_class_for_output()` to change this behavior.
* `lookup_field` - The model field that should be used to for performing object lookup of individual model instances. Defaults to `'pk'`. Note that when using hyperlinked APIs you'll need to ensure that *both* the API views *and* the serializer classes set the lookup fields if you need to use a custom value. * `lookup_field` - The model field that should be used to for performing object lookup of individual model instances. Defaults to `'pk'`. Note that when using hyperlinked APIs you'll need to ensure that *both* the API views *and* the serializer classes set the lookup fields if you need to use a custom value.
* `lookup_url_kwarg` - The URL keyword argument that should be used for object lookup. The URL conf should include a keyword argument corresponding to this value. If unset this defaults to using the same value as `lookup_field`. * `lookup_url_kwarg` - The URL keyword argument that should be used for object lookup. The URL conf should include a keyword argument corresponding to this value. If unset this defaults to using the same value as `lookup_field`.
@ -141,7 +142,7 @@ For example:
Returns the class that should be used for the serializer. Defaults to returning the `serializer_class` attribute, or dynamically generating a serializer class if the `model` shortcut is being used. Returns the class that should be used for the serializer. Defaults to returning the `serializer_class` attribute, or dynamically generating a serializer class if the `model` shortcut is being used.
May be override to provide dynamic behavior such as using different serializers for read and write operations, or providing different serializers to different types of users. May be override to provide dynamic behavior such as providing different serializers to different types of users. To usie different serializers for read and write operations, see `get_serializer_class_for_output`.
For example: For example:
@ -150,6 +151,36 @@ For example:
return FullAccountSerializer return FullAccountSerializer
return BasicAccountSerializer return BasicAccountSerializer
#### `get_serializer_class_for_output(self)`
Returns the class that should be used for the serializer when deserializing objects. By default this returns the `serializer_class_for_output` attribute. If this attribute is not set, this is identical to `get_serializer_class`.
This method allows you to use a different serializer for read and write operations on objects. For example:
class Album(Model):
title = CharField(max_length=127)
class Track(Model):
title = CharField(max_length=127)
album = ForeignKey(Album, related_name='tracks')
class TrackSerializer(ModelSerializer):
class Meta:
fields = ('title', 'album')
class TrackSerializerWithDetails(ModelSerializer):
album = AlbumSerializer()
class Meta:
fields = ('title', 'album')
In this situation you may wish to display the full album information in the Track view, but you want the album field to be editable, and you don't want to supply a full Album object when updating or creating a Track. This is easily done by creating your ModelViewSet as such:
class TrackViewSet(ModelViewSet):
queryset = Track.objects.all()
serializer_class = TrackSerializer
serializer_class_for_output = TrackSerializerWithDetails
#### `get_paginate_by(self)` #### `get_paginate_by(self)`
Returns the page size to use with pagination. By default this uses the `paginate_by` attribute, and may be overridden by the client if the `paginate_by_param` attribute is set. Returns the page size to use with pagination. By default this uses the `paginate_by` attribute, and may be overridden by the client if the `paginate_by_param` attribute is set.

View File

@ -46,6 +46,11 @@ class GenericAPIView(views.APIView):
queryset = None queryset = None
serializer_class = None serializer_class = None
# You may set this attribute to use a different serializer for
# deserializing objects. You can override
# `get_serializer_class_for_output()` instead of setting it.
serializer_class_for_output = None
# This shortcut may be used instead of setting either or both # This shortcut may be used instead of setting either or both
# of the `queryset`/`serializer_class` attributes, although using # of the `queryset`/`serializer_class` attributes, although using
# the explicit style is generally preferred. # the explicit style is generally preferred.
@ -94,7 +99,7 @@ class GenericAPIView(views.APIView):
partial=False, allow_add_remove=False): partial=False, allow_add_remove=False):
""" """
Return the serializer instance that should be used for validating and Return the serializer instance that should be used for validating and
deserializing input, and for serializing output. deserializing input.
""" """
serializer_class = self.get_serializer_class() serializer_class = self.get_serializer_class()
context = self.get_serializer_context() context = self.get_serializer_context()
@ -103,13 +108,23 @@ class GenericAPIView(views.APIView):
allow_add_remove=allow_add_remove, allow_add_remove=allow_add_remove,
context=context) context=context)
def get_serializer_for_output(self, instance=None, many=False):
"""
Return the serializer instance that should be used for
serializing output. Defaults to the serializer instance used
for deserializing input.
"""
serializer_class = self.get_serializer_class_for_output()
context = self.get_serializer_context()
return serializer_class(instance, context=context, many=many)
def get_pagination_serializer(self, page): def get_pagination_serializer(self, page):
""" """
Return a serializer instance to use with paginated data. Return a serializer instance to use with paginated data.
""" """
class SerializerClass(self.pagination_serializer_class): class SerializerClass(self.pagination_serializer_class):
class Meta: class Meta:
object_serializer_class = self.get_serializer_class() object_serializer_class = self.get_serializer_class_for_output()
pagination_serializer_class = SerializerClass pagination_serializer_class = SerializerClass
context = self.get_serializer_context() context = self.get_serializer_context()
@ -252,6 +267,19 @@ class GenericAPIView(views.APIView):
model = self.model model = self.model
return DefaultSerializer return DefaultSerializer
def get_serializer_class_for_output(self):
"""
Return the class to use for the serializer when deserializing
objects.
Defaults to using `self.serializer_class_for_output`, if None,
defaults to `self.get_serializer()`.
"""
serializer_class = self.serializer_class_for_output
if serializer_class is None:
return self.get_serializer_class()
return serializer_class
def get_queryset(self): def get_queryset(self):
""" """
Get the list of items for this view. Get the list of items for this view.

View File

@ -53,8 +53,9 @@ class CreateModelMixin(object):
self.pre_save(serializer.object) self.pre_save(serializer.object)
self.object = serializer.save(force_insert=True) self.object = serializer.save(force_insert=True)
self.post_save(self.object, created=True) self.post_save(self.object, created=True)
out_serializer = self.get_serializer_for_output(self.object)
headers = self.get_success_headers(serializer.data) headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, return Response(out_serializer.data, status=status.HTTP_201_CREATED,
headers=headers) headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@ -93,7 +94,8 @@ class ListModelMixin(object):
if page is not None: if page is not None:
serializer = self.get_pagination_serializer(page) serializer = self.get_pagination_serializer(page)
else: else:
serializer = self.get_serializer(self.object_list, many=True) serializer = self.get_serializer_for_output(
self.object_list, many=True)
return Response(serializer.data) return Response(serializer.data)
@ -104,7 +106,7 @@ class RetrieveModelMixin(object):
""" """
def retrieve(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
serializer = self.get_serializer(self.object) serializer = self.get_serializer_for_output(self.object)
return Response(serializer.data) return Response(serializer.data)
@ -136,7 +138,8 @@ class UpdateModelMixin(object):
self.object = serializer.save(force_update=True) self.object = serializer.save(force_update=True)
self.post_save(self.object, created=False) self.post_save(self.object, created=False)
return Response(serializer.data, status=status.HTTP_200_OK) out_serializer = self.get_serializer_for_output(self.object)
return Response(out_serializer.data, status=status.HTTP_200_OK)
def partial_update(self, request, *args, **kwargs): def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True kwargs['partial'] = True