aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Christie2012-09-03 17:49:22 +0100
committerTom Christie2012-09-03 17:49:22 +0100
commit6e21915934686cc7d46c8144403c933fa6fd2375 (patch)
treea11ccaa21cc735b3223cb6d81796e1ebe6bf84c2
parenta092a72844705e3129b8996b81d8424997b5d37f (diff)
downloaddjango-rest-framework-6e21915934686cc7d46c8144403c933fa6fd2375.tar.bz2
First pass at mixins & generic views
-rw-r--r--djangorestframework/mixins.py64
-rw-r--r--djangorestframework/views.py72
-rw-r--r--docs/tutorial/3-class-based-views.md43
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