text: """
  Basic building blocks for generic class based views.
  
  We don't bind behaviour to http method handlers yet,
  which allows mixin classes to be composed in interesting ways.
  """
  from __future__ import unicode_literals
  
  from django.http import Http404
  from rest_framework import status
  from rest_framework.response import Response
  from rest_framework.request import clone_request
  
  
  def _get_validation_exclusions(obj, pk=None, slug_field=None):
      """
      Given a model instance, and an optional pk and slug field,
      return the full list of all other field names on that model.
  
      For use when performing full_clean on a model instance,
      so we only clean the required fields.
      """
      include = []
  
      if pk:
          pk_field = obj._meta.pk
          while pk_field.rel:
              pk_field = pk_field.rel.to._meta.pk
          include.append(pk_field.name)
  
      if slug_field:
          include.append(slug_field)
  
      return [field.name for field in obj._meta.fields if field.name not in include]
  
  
  class CreateModelMixin(object):
      """
      Create a model instance.
      Should be mixed in with any `GenericAPIView`.
      """
      def create(self, request, *args, **kwargs):
          serializer = self.get_serializer(data=request.DATA, files=request.FILES)
  
          if serializer.is_valid():
              self.pre_save(serializer.object)
              self.object = serializer.save(force_insert=True)
              self.post_save(self.object, created=True)
              headers = self.get_success_headers(serializer.data)
              return Response(serializer.data, status=status.HTTP_201_CREATED,
                              headers=headers)
  
          return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  
      def get_success_headers(self, data):
          try:
              return {'Location': data['url']}
          except (TypeError, KeyError):
              return {}
  
  
  class ListModelMixin(object):
      """
      List a queryset.
      Should be mixed in with `MultipleObjectAPIView`.
      """
      empty_error = "Empty list and '%(class_name)s.allow_empty' is False."
  
      def list(self, request, *args, **kwargs):
          queryset = self.get_queryset()
          self.object_list = self.filter_queryset(queryset)
  
          # Default is to allow empty querysets.  This can be altered by setting
          # `.allow_empty = False`, to raise 404 errors on empty querysets.
          allow_empty = self.get_allow_empty()
          if not allow_empty and not self.object_list:
              class_name = self.__class__.__name__
              error_msg = self.empty_error % {'class_name': class_name}
              raise Http404(error_msg)
  
          # Pagination size is set by the `.paginate_by` attribute,
          # which may be `None` to disable pagination.
          page_size = self.get_paginate_by(self.object_list)
          if page_size:
              packed = self.paginate_queryset(self.object_list, page_size)
              paginator, page, queryset, is_paginated = packed
              serializer = self.get_pagination_serializer(page)
          else:
              serializer = self.get_serializer(self.object_list, many=True)
  
          return Response(serializer.data)
  
  
  class RetrieveModelMixin(object):
      """
      Retrieve a model instance.
      Should be mixed in with `SingleObjectAPIView`.
      """
      def retrieve(self, request, *args, **kwargs):
          self.object = self.get_object()
          serializer = self.get_serializer(self.object)
          return Response(serializer.data)
  
  
  class UpdateModelMixin(object):
      """
      Update a model instance.
      Should be mixed in with `SingleObjectAPIView`.
      """
      def update(self, request, *args, **kwargs):
          partial = kwargs.pop('partial', False)
          self.object = None
          try:
              self.object = self.get_object()
          except Http404:
              # If this is a PUT-as-create operation, we need to ensure that
              # we have relevant permissions, as if this was a POST request.
              self.check_permissions(clone_request(request, 'POST'))
              created = True
              save_kwargs = {'force_insert': True}
              success_status_code = status.HTTP_201_CREATED
          else:
              created = False
              save_kwargs = {'force_update': True}
              success_status_code = status.HTTP_200_OK
  
          serializer = self.get_serializer(self.object, data=request.DATA,
                                           files=request.FILES, partial=partial)
  
          if serializer.is_valid():
              self.pre_save(serializer.object)
              self.object = serializer.save(**save_kwargs)
              self.post_save(self.object, created=created)
              return Response(serializer.data, status=success_status_code)
  
          return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  
      def pre_save(self, obj):
          """
          Set any attributes on the object that are implicit in the request.
          """
          # pk and/or slug attributes are implicit in the URL.
          pk = self.kwargs.get(self.pk_url_kwarg, None)
          slug = self.kwargs.get(self.slug_url_kwarg, None)
          slug_field = slug and self.get_slug_field() or None
  
          if pk:
              setattr(obj, 'pk', pk)
  
          if slug:
              setattr(obj, slug_field, slug)
  
          # Ensure we clean the attributes so that we don't eg return integer
          # pk using a string representation, as provided by the url conf kwarg.
          if hasattr(obj, 'full_clean'):
              exclude = _get_validation_exclusions(obj, pk, slug_field)
              obj.full_clean(exclude)
  
  
  class DestroyModelMixin(object):
      """
      Destroy a model instance.
      Should be mixed in with `SingleObjectAPIView`.
      """
      def destroy(self, request, *args, **kwargs):
          obj = self.get_object()
          obj.delete()
          return Response(status=status.HTTP_204_NO_CONTENT)
  