diff options
| -rw-r--r-- | djangorestframework/mixins.py | 64 | ||||
| -rw-r--r-- | djangorestframework/views.py | 72 | ||||
| -rw-r--r-- | docs/tutorial/3-class-based-views.md | 43 | 
3 files changed, 162 insertions, 17 deletions
diff --git a/djangorestframework/mixins.py b/djangorestframework/mixins.py index e69de29b..2721f59e 100644 --- a/djangorestframework/mixins.py +++ b/djangorestframework/mixins.py @@ -0,0 +1,64 @@ +from djangorestframework import status +from djangorestframework.response import Response + + +class CreateModelMixin(object): +    """ +    Create a model instance. +    Should be mixed in with any `APIView` +    """ +    def create(self, request, *args, **kwargs): +        serializer = self.get_serializer(data=request.DATA) +        if serializer.is_valid(): +            self.object = serializer.object +            self.object.save() +            return Response(serializer.data, status=status.HTTP_201_CREATED) +        return Response(serializer.error_data, status=status.HTTP_400_BAD_REQUEST) + + +class ListModelMixin(object): +    """ +    List a queryset. +    Should be mixed in with `MultipleObjectBaseView`. +    """ +    def list(self, request, *args, **kwargs): +        self.object_list = self.get_queryset() +        serializer = self.get_serializer(instance=self.object_list) +        return Response(serializer.data) + + +class RetrieveModelMixin(object): +    """ +    Retrieve a model instance. +    Should be mixed in with `SingleObjectBaseView`. +    """ +    def retrieve(self, request, *args, **kwargs): +        self.object = self.get_object() +        serializer = self.get_serializer(instance=self.object) +        return Response(serializer.data) + + +class UpdateModelMixin(object): +    """ +    Update a model instance. +    Should be mixed in with `SingleObjectBaseView`. +    """ +    def update(self, request, *args, **kwargs): +        self.object = self.get_object() +        serializer = self.get_serializer(data=request.DATA, instance=self.object) +        if serializer.is_valid(): +            self.object = serializer.deserialized +            self.object.save() +            return Response(serializer.data) +        return Response(serializer.error_data, status=status.HTTP_400_BAD_REQUEST) + + +class DestroyModelMixin(object): +    """ +    Destroy a model instance. +    Should be mixed in with `SingleObjectBaseView`. +    """ +    def destroy(self, request, *args, **kwargs): +        self.object = self.get_object() +        self.object.delete() +        return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/djangorestframework/views.py b/djangorestframework/views.py index 389f2044..1939eed2 100644 --- a/djangorestframework/views.py +++ b/djangorestframework/views.py @@ -11,12 +11,14 @@ from django.http import Http404  from django.utils.html import escape  from django.utils.safestring import mark_safe  from django.views.decorators.csrf import csrf_exempt +from django.views.generic.detail import SingleObjectMixin +from django.views.generic.list import MultipleObjectMixin  from djangorestframework.compat import View as _View, apply_markdown  from djangorestframework.response import Response  from djangorestframework.request import Request  from djangorestframework.settings import api_settings -from djangorestframework import parsers, authentication, permissions, status, exceptions +from djangorestframework import parsers, authentication, permissions, status, exceptions, mixins  __all__ = ( @@ -281,3 +283,71 @@ class APIView(_View):                  field_name_types[name] = field.__class__.__name__              content['fields'] = field_name_types          raise Response(content, status=status.HTTP_200_OK) + +# TODO: .get_serializer() + + +### Abstract view classes, that do not provide any method handlers ### + +class MultipleObjectBaseView(MultipleObjectMixin, APIView): +    """ +    Base class for views onto a queryset. +    """ +    pass + + +class SingleObjectBaseView(SingleObjectMixin, APIView): +    """ +    Base class for views onto a model instance. +    """ +    pass + + +### Concrete view classes, that provide existing method handlers ### + +class ListAPIView(mixins.ListModelMixin, +                  MultipleObjectBaseView): +    """ +    Concrete view for listing a queryset. +    """ +    def get(self, request, *args, **kwargs): +        return self.list(request, *args, **kwargs) + + +class RootAPIView(mixins.ListModelMixin, +                  mixins.CreateModelMixin, +                  MultipleObjectBaseView): +    """ +    Concrete view for listing a queryset or creating a model instance. +    """ +    def get(self, request, *args, **kwargs): +        return self.list(request, *args, **kwargs) + +    def post(self, request, *args, **kwargs): +        return self.create(request, *args, **kwargs) + + +class DetailAPIView(mixins.RetrieveModelMixin, +                      SingleObjectBaseView): +    """ +    Concrete view for retrieving a model instance. +    """ +    def get(self, request, *args, **kwargs): +        return self.retrieve(request, *args, **kwargs) + + +class InstanceAPIView(mixins.RetrieveModelMixin, +                      mixins.UpdateModelMixin, +                      mixins.DestroyModelMixin, +                      SingleObjectBaseView): +    """ +    Concrete view for retrieving, updating or deleting a model instance. +    """ +    def get(self, request, *args, **kwargs): +        return self.retrieve(request, *args, **kwargs) + +    def put(self, request, *args, **kwargs): +        return self.update(request, *args, **kwargs) + +    def delete(self, request, *args, **kwargs): +        return self.destroy(request, *args, **kwargs) diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md index 616a6058..21255a68 100644 --- a/docs/tutorial/3-class-based-views.md +++ b/docs/tutorial/3-class-based-views.md @@ -57,7 +57,7 @@ So far, so good.  It looks pretty similar to the previous case, but we've got be                  comment = serializer.deserialized                  comment.save()                  return Response(serializer.data) -            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) +            return Response(serializer.error_data, status=status.HTTP_400_BAD_REQUEST)          def delete(self, request, pk, format=None):              comment = self.get_object(pk) @@ -79,27 +79,38 @@ We can compose those mixin classes, to recreate our existing API behaviour with      from blog.models import Comment      from blog.serializers import CommentSerializer -    from djangorestframework import mixins, views +    from djangorestframework import mixins +    from djangorestframework import views + -    class CommentRoot(mixins.ListModelQuerysetMixin, -                      mixins.CreateModelInstanceMixin, -                      views.BaseRootAPIView): +    class CommentRoot(mixins.ListModelMixin, +                      mixins.CreateModelMixin, +                      views.MultipleObjectBaseView):          model = Comment          serializer_class = CommentSerializer -        get = list -        post = create +        def get(self, request, *args, **kwargs): +            return self.list(request, *args, **kwargs) + +        def post(self, request, *args, **kwargs): +            return self.create(request, *args, **kwargs) -    class CommentInstance(mixins.RetrieveModelInstanceMixin, -                          mixins.UpdateModelInstanceMixin, -                          mixins.DestroyModelInstanceMixin, -                          views.BaseInstanceAPIView): + +    class CommentInstance(mixins.RetrieveModelMixin, +                          mixins.UpdateModelMixin, +                          mixins.DestroyModelMixin, +                          views.SingleObjectBaseView):          model = Comment          serializer_class = CommentSerializer -        get = retrieve -        put = update -        delete = destroy +        def get(self, request, *args, **kwargs): +            return self.retrieve(request, *args, **kwargs) + +        def put(self, request, *args, **kwargs): +            return self.update(request, *args, **kwargs) + +        def delete(self, request, *args, **kwargs): +            return self.destroy(request, *args, **kwargs)  ## Reusing generic class based views @@ -109,11 +120,11 @@ That's a lot less code than before, but we can go one step further still.  REST      from blog.serializers import CommentSerializer      from djangorestframework import views -    class CommentRoot(views.RootAPIView): +    class CommentRoot(views.RootModelView):          model = Comment          serializer_class = CommentSerializer -    class CommentInstance(views.InstanceAPIView): +    class CommentInstance(views.InstanceModelView):          model = Comment          serializer_class = CommentSerializer  | 
