diff options
Diffstat (limited to 'docs/tutorial/3-class-based-views.md')
| -rw-r--r-- | docs/tutorial/3-class-based-views.md | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md new file mode 100644 index 00000000..f27b5af0 --- /dev/null +++ b/docs/tutorial/3-class-based-views.md @@ -0,0 +1,150 @@ +# Tutorial 3: Class Based Views + +We can also write our API views using class based views, rather than function based views. As we'll see this is a powerful pattern that allows us to reuse common functionality, and helps us keep our code [DRY][dry]. + +## Rewriting our API using class based views + +We'll start by rewriting the root view as a class based view. All this involves is a little bit of refactoring. + + from snippet.models import Snippet + from snippet.serializers import SnippetSerializer + from django.http import Http404 + from rest_framework.views import APIView + from rest_framework.response import Response + from rest_framework import status + + + class SnippetList(APIView): + """ + List all snippets, or create a new snippet. + """ + def get(self, request, format=None): + snippets = Snippet.objects.all() + serializer = SnippetSerializer(instance=snippets) + return Response(serializer.data) + + def post(self, request, format=None): + serializer = SnippetSerializer(request.DATA) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +So far, so good. It looks pretty similar to the previous case, but we've got better separation between the different HTTP methods. We'll also need to update the instance view. + + class SnippetDetail(APIView): + """ + Retrieve, update or delete a snippet instance. + """ + def get_object(self, pk): + try: + return Snippet.objects.get(pk=pk) + except Snippet.DoesNotExist: + raise Http404 + + def get(self, request, pk, format=None): + snippet = self.get_object(pk) + serializer = SnippetSerializer(instance=snippet) + return Response(serializer.data) + + def put(self, request, pk, format=None): + snippet = self.get_object(pk) + serializer = SnippetSerializer(request.DATA, instance=snippet) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, pk, format=None): + snippet = self.get_object(pk) + snippet.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + +That's looking good. Again, it's still pretty similar to the function based view right now. + +We'll also need to refactor our URLconf slightly now we're using class based views. + + from django.conf.urls import patterns, url + from rest_framework.urlpatterns import format_suffix_patterns + from snippetpost import views + + urlpatterns = patterns('', + url(r'^$', views.SnippetList.as_view()), + url(r'^(?P<pk>[0-9]+)$', views.SnippetDetail.as_view()) + ) + + urlpatterns = format_suffix_patterns(urlpatterns) + +Okay, we're done. If you run the development server everything should be working just as before. + +## Using mixins + +One of the big wins of using class based views is that it allows us to easily compose reusable bits of behaviour. + +The create/retrieve/update/delete operations that we've been using so far are going to be pretty simliar for any model-backed API views we create. Those bits of common behaviour are implemented in REST framework's mixin classes. + +Let's take a look at how we can compose our views by using the mixin classes. + + from snippet.models import Snippet + from snippet.serializers import SnippetSerializer + from rest_framework import mixins + from rest_framework import generics + + class SnippetList(mixins.ListModelMixin, + mixins.CreateModelMixin, + generics.MultipleObjectBaseView): + model = Snippet + serializer_class = SnippetSerializer + + def get(self, request, *args, **kwargs): + return self.list(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + return self.create(request, *args, **kwargs) + +We'll take a moment to examine exactly what's happening here - We're building our view using `MultipleObjectBaseView`, and adding in `ListModelMixin` and `CreateModelMixin`. + +The base class provides the core functionality, and the mixin classes provide the `.list()` and `.create()` actions. We're then explicitly binding the `get` and `post` methods to the appropriate actions. Simple enough stuff so far. + + class SnippetDetail(mixins.RetrieveModelMixin, + mixins.UpdateModelMixin, + mixins.DestroyModelMixin, + generics.SingleObjectBaseView): + model = Snippet + serializer_class = SnippetSerializer + + 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) + +Pretty similar. This time we're using the `SingleObjectBaseView` class to provide the core functionality, and adding in mixins to provide the `.retrieve()`, `.update()` and `.destroy()` actions. + +## Using generic class based views + +Using the mixin classes we've rewritten the views to use slightly less code than before, but we can go one step further. REST framework provides a set of already mixed-in generic views that we can use. + + from snippet.models import Snippet + from snippet.serializers import SnippetSerializer + from rest_framework import generics + + + class SnippetList(generics.ListCreateAPIView): + model = Snippet + serializer_class = SnippetSerializer + + + class SnippetDetail(generics.RetrieveUpdateDestroyAPIView): + model = Snippet + serializer_class = SnippetSerializer + +Wow, that's pretty concise. We've got a huge amount for free, and our code looks like good, clean, idiomatic Django. + +Next we'll move onto [part 4 of the tutorial][tut-4], where we'll take a look at how we can deal with authentication and permissions for our API. + +[dry]: http://en.wikipedia.org/wiki/Don't_repeat_yourself +[tut-4]: 4-authentication-and-permissions.md |
