2016-07-08 03:38:50 -04:00
# Tutorial 3: Class-based Views
2012-08-29 20:57:37 +01:00
2016-07-08 03:38:50 -04:00
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].
2012-08-29 20:57:37 +01:00
2016-07-08 03:38:50 -04:00
## Rewriting our API using class-based views
2012-08-29 20:57:37 +01:00
2016-07-08 03:38:50 -04:00
We'll start by rewriting the root view as a class-based view. All this involves is a little bit of refactoring of `views.py` .
2012-08-29 20:57:37 +01:00
2025-10-14 11:31:35 +05:30
``` python
from snippets . models import Snippet
from snippets . 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 ( snippets , many = True )
return Response ( serializer . data )
def post ( self , request , format = None ) :
serializer = SnippetSerializer ( data = 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 )
```
2012-09-03 16:42:57 +01:00
2014-08-15 20:45:28 -06:00
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 in `views.py` .
2012-08-29 20:57:37 +01:00
2025-10-14 11:31:35 +05:30
``` python
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 ( snippet )
return Response ( serializer . data )
def put ( self , request , pk , format = None ) :
snippet = self . get_object ( pk )
serializer = SnippetSerializer ( snippet , data = request . data )
if serializer . is_valid ( ) :
serializer . save ( )
2012-08-29 20:57:37 +01:00
return Response ( serializer . data )
2025-10-14 11:31:35 +05:30
return Response ( serializer . errors , status = status . HTTP_400_BAD_REQUEST )
2012-09-07 22:53:02 +02:00
2025-10-14 11:31:35 +05:30
def delete ( self , request , pk , format = None ) :
snippet = self . get_object ( pk )
snippet . delete ( )
return Response ( status = status . HTTP_204_NO_CONTENT )
```
2012-08-29 20:57:37 +01:00
2012-09-03 16:42:57 +01:00
That's looking good. Again, it's still pretty similar to the function based view right now.
2012-09-17 20:19:45 +01:00
2017-11-27 05:34:17 -05:00
We'll also need to refactor our `snippets/urls.py` slightly now that we're using class-based views.
2012-09-17 20:19:45 +01:00
2025-10-14 11:31:35 +05:30
``` python
from django . urls import path
from rest_framework . urlpatterns import format_suffix_patterns
from snippets import views
2012-09-17 20:19:45 +01:00
2025-10-14 11:31:35 +05:30
urlpatterns = [
path ( " snippets/ " , views . SnippetList . as_view ( ) ) ,
path ( " snippets/<int:pk>/ " , views . SnippetDetail . as_view ( ) ) ,
]
2014-08-15 20:45:28 -06:00
2025-10-14 11:31:35 +05:30
urlpatterns = format_suffix_patterns ( urlpatterns )
```
2012-09-17 20:19:45 +01:00
2012-08-29 20:57:37 +01:00
Okay, we're done. If you run the development server everything should be working just as before.
## Using mixins
2022-10-19 07:20:11 -03:00
One of the big wins of using class-based views is that it allows us to easily compose reusable bits of behavior.
2012-08-29 20:57:37 +01:00
2022-10-19 07:20:11 -03:00
The create/retrieve/update/delete operations that we've been using so far are going to be pretty similar for any model-backed API views we create. Those bits of common behavior are implemented in REST framework's mixin classes.
2012-08-29 20:57:37 +01:00
2013-10-18 09:32:04 +01:00
Let's take a look at how we can compose the views by using the mixin classes. Here's our `views.py` module again.
2012-08-29 20:57:37 +01:00
2025-10-14 11:31:35 +05:30
``` python
from snippets . models import Snippet
from snippets . serializers import SnippetSerializer
from rest_framework import mixins
from rest_framework import generics
2012-08-29 20:57:37 +01:00
2025-10-14 11:31:35 +05:30
class SnippetList (
mixins . ListModelMixin , mixins . CreateModelMixin , generics . GenericAPIView
) :
queryset = Snippet . objects . all ( )
serializer_class = SnippetSerializer
2012-08-29 20:57:37 +01:00
2025-10-14 11:31:35 +05:30
def get ( self , request , * args , * * kwargs ) :
return self . list ( request , * args , * * kwargs )
2012-09-03 17:49:22 +01:00
2025-10-14 11:31:35 +05:30
def post ( self , request , * args , * * kwargs ) :
return self . create ( request , * args , * * kwargs )
```
2012-08-29 20:57:37 +01:00
2013-05-28 17:13:12 +02:00
We'll take a moment to examine exactly what's happening here. We're building our view using `GenericAPIView` , and adding in `ListModelMixin` and `CreateModelMixin` .
2012-09-07 09:37:06 +01:00
2012-10-21 16:34:07 +02:00
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.
2012-09-03 17:49:22 +01:00
2025-10-14 11:31:35 +05:30
``` python
class SnippetDetail (
mixins . RetrieveModelMixin ,
mixins . UpdateModelMixin ,
mixins . DestroyModelMixin ,
generics . GenericAPIView ,
) :
queryset = Snippet . objects . all ( )
serializer_class = SnippetSerializer
2012-08-29 20:57:37 +01:00
2025-10-14 11:31:35 +05:30
def get ( self , request , * args , * * kwargs ) :
return self . retrieve ( request , * args , * * kwargs )
2012-09-03 17:49:22 +01:00
2025-10-14 11:31:35 +05:30
def put ( self , request , * args , * * kwargs ) :
return self . update ( request , * args , * * kwargs )
2012-09-03 17:49:22 +01:00
2025-10-14 11:31:35 +05:30
def delete ( self , request , * args , * * kwargs ) :
return self . destroy ( request , * args , * * kwargs )
```
2012-08-29 20:57:37 +01:00
2013-04-24 22:40:24 +01:00
Pretty similar. Again we're using the `GenericAPIView` class to provide the core functionality, and adding in mixins to provide the `.retrieve()` , `.update()` and `.destroy()` actions.
2012-09-07 09:37:06 +01:00
2016-07-08 03:38:50 -04:00
## Using generic class-based views
2012-08-29 20:57:37 +01:00
2013-10-18 09:32:04 +01:00
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 to trim down our `views.py` module even more.
2012-08-29 20:57:37 +01:00
2025-10-14 11:31:35 +05:30
``` python
from snippets . models import Snippet
from snippets . serializers import SnippetSerializer
from rest_framework import generics
2012-08-29 20:57:37 +01:00
2012-09-07 09:37:06 +01:00
2025-10-14 11:31:35 +05:30
class SnippetList ( generics . ListCreateAPIView ) :
queryset = Snippet . objects . all ( )
serializer_class = SnippetSerializer
2012-08-29 20:57:37 +01:00
2012-09-07 09:37:06 +01:00
2025-10-14 11:31:35 +05:30
class SnippetDetail ( generics . RetrieveUpdateDestroyAPIView ) :
queryset = Snippet . objects . all ( )
serializer_class = SnippetSerializer
```
2012-08-29 20:57:37 +01:00
2012-12-05 16:08:13 +01:00
Wow, that's pretty concise. We've gotten a huge amount for free, and our code looks like good, clean, idiomatic Django.
2012-08-29 20:57:37 +01:00
2012-10-28 19:25:51 +00:00
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.
2012-08-29 20:57:37 +01:00
2018-01-08 07:22:32 -08:00
[dry]: https://en.wikipedia.org/wiki/Don't_repeat_yourself
2012-10-28 19:25:51 +00:00
[tut-4]: 4-authentication-and-permissions.md