##Table of Contents
##Summary
####REST
Representational State Transfer (REST) is a software architecture style that includes best practices for scaling web services. The idea is that client-server is separated for portability of code and ability to scale. REST systems communicate over Hypertext Transfer Protocol and uses commands like (GET, POST) to retrieve and send data. REST basically exposes application data through an API and with django, it’s easy to use django-rest-framework to expose our models and views.
####Django REST Framework (DRF)
Django REST Framework (DRF) is a quick and standard way of building REST APIs. It has a browsable GUI interface, many built-in forms of authentication, and supports many types of serialization. You don’t technically need to use this to make an API (instead send payloads with requests and responses), but DRF is recommended since it is standardized.
In Django, you have data in Model objects/instances and querysets. Serializers allow you to convert from these objects and querysets into native Python that can then be rendered into web friendly formats like JSON.
####DRF Serializer Relationships
While creating your serializers, you’ll want to know their relationships (just like in Django how you define the model relationships). You can always check your current relationships in Django and DRF by using python manage.py shell
to import, instantiate, and print repr(myserializer)
or print repr(mymodel)
. For example:
>>> from myapp.serializers import StuffSerializer
>>> serializer = StuffSerializer()
>>> print repr(serializer)
Here we’ll see what the possible serializer relationships are:
StringRelatedField
represents the target of the relationship using its __unicode__
method in the Django Model. Pretty straightforward; if my model has a unicode of ‘Stuff’, the serializer output shows ‘Stuff’.
PrimaryKeyRelatedField
represents the target of the relationship usings its primary key. So say we have a Django Model with a primary key (usually its the id
field) with a value of 10. Our serializer now shows the field as 10.
HyperlinkedRelatedField
represents the target of a relationship using a hyperlink. This field is just a hyperlink of where the object is, e.g. http://williamqliu.com/stuff/10
.
SlugRelatedField
represents the target of a relationship using a field on the target. So this one is basically creating a new field in your serializer that is an existing field in another model. Default is Read and Write (though you can change flag to read_only
if you want).
####Hyperlinked Identity Field
HyperlinkedIdentityField
represents the target of a relationship using a field on a hyperlink’s identity. This field is applied to an identity relationship (e.g. the url) or added as an attribute to your serializer. Always Read Only.
You can nest relationships by using serializers as fields. By default, these are read only. If you want to be able to write to them, then you need to create either or both create()
and/or update
methods to explicitly specify how the child relationships should be saved.
So there’s a few different ways that DRF gives you to create views. Starting from most generic to more specific, we have the following:
.list()
, .create()
instead of HTTP methods (e.g. .get()
or .post()
). The idea is that you can mix and match Mixins to your liking or use one of those pre-built views. For example, a CreateAPIView
is only a GenericAPIView
+ CreateModelMixin
. In my opinion, using something like CreateAPIView
is a little confusing; either use GenericAPIView or use APIView..get()
or .post()
). You return a rest_framework Response()
. This is more detailed / specific than a GenericAPIView.request.method == 'POST'
, etc.####DRF APIGenericView (i.e. a Base View Class)
With a GenericAPIView, you have a skeleton API View that has things every View would need, like what the queryset is, the serializer_class, etc. From this basic skeleton, you can add in some built-in functionality for the most common operations with Mixins (e.g. CreateModelMixin
). When you combine the GenericAPIView with Mixins, you get some generic views (e.g. DestroyAPIView
= GenericAPIView
+ DestroyModelMixin
. You basically set some class attributes and override the view functions as needed. Since this is a Generic API View, we define the http methods (e.g. GET, POST, PATCH) and then we call the Mixin’s actions (e.g. list, create, retrieve, update).
####DRF Generic View - Using Mixins
Mixins are reusable components for class based views. We add Mixins to a GenericAPIView class so that the class can gain additional functionality, in this case it provides an action that has .something()
functionality where something is say .list()
, .create()
, .retrieve()
, .update()
, .destroy()
. Note that these actions are abstractions of http methods (like GET, POST). That means we define say our GET, then when we return we use the .list()
functionality. In this example, the ListModelMixin
gives the .list()
functionality and the CreateModelMixin
gives the .create()
functionality to our GenericAPIView.
from rest_framework import mixins, generics
from .models import Post
from .serializers import PostSerializer
class PostListCreate(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
This is the same as the Concrete View below (though I prefer to stay away from the Concrete View):
class PostListCreate(generics.ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
def list(self, request): # define to override list
queryset = self.get_queryset()
serializer = PostSerializer(queryset)
return Response(serializer.data)
I prefer not to use a Concrete View like ListCreateAPIView
or UpdateAPIView
. The reasoning is that we abstract away from http_methods (e.g. GET, POST) and instead only define the actions (e.g. list, retrieve). To me, it’s difficult to go from the action (e.g. list) and tie that to the http method (e.g. GET)
from django.contrib.auth.models import User
from rest_framework import generics
from rest_framework.permissions import IsAdminUser
from myapp.serializers import UserSerializer
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (IsAdminUser,)
paginate_by = 100
def get_queryset(self):
""" Override default queryset to only get user's account """
user = self.request.user
return user.accounts.all()
def list(self, request):
queryset = self.get_queryset()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data)
####DRF APIView
The class based APIView is a great balance between boilerplate and custom code.
from rest_framework.views import APIView
from rest_framework.response import Response
from myapp.serializers import PostSerializer
from myapp.models import Post
class ListPost(APIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
def get(self, request, format=None):
queryset = Post.objects.all()
serializer = PostSerializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
####DRF GenericAPIView and APIView Routing
In your main urls.py file, link to your existing app api urls.py.
#urls.py
urlpatterns = patterns('',
(r'^api/', include(patterns('',
url(r'^blog/', include('blog.api.urls')),
url(r'^accounts/', include('accounts.api.urls')),
), namespace='api')),
...
####DRF ViewSets
DRF allows you to combine the logic for a set of related views into a single class called a ViewSet (aka ‘Resources’, ‘Controllers’). A ViewSet is simply a Class Based View that does not provide any method handlers (like .get()
, .post()
) and instead provides actions (like .list()
, .create()
). You have your standard ViewSet (inherits from APIView), GenericViewSet (inherits from GenericAPIView), and ModelViewSet (inherits from GenericAPIView).
from django.contrib.auth.models import User
from django.shortcut import get_object_or_404
from myapp.serializers import UserSerializer
from rest_framework import viewsets
from rest_framework.response import Response
class UserViewSet(viewsets.ViewSet):
def list(self, request):
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = User.objects.all()
user = get_object_or_404(queryset, pk=pk)
serializer = UserSerializer(user)
return Response(serializer.data)
Personally I think this has too much magic and you should avoid viewsets; instead opt for APIGenericView with Mixins or APIView.
####DRF Routers
You can wire up your urls manually or if you used a ViewSet, you can then use DRF’s routers to automatically do URL routing for you. There’s a couple of default routers including SimpleRouter and DefaultRouter. In my opinion, don’t use ViewSets because of the additional magic (so thus you can’t use these router functions), but the patterns here are good to know and follow since they offer a standardized approach to API design.
from rest_framework import routers
router = routers.SimpleRouter()
router.register(r'users', UserViewSet)
urlpatterns += router.urls
We pass in a prefix (in this case users
) and the ViewSet. For example, the pattern for the above SimpleRouter is this:
^users/$
Name: user-list
# from UserViewSet.list^users/{pk}/$
Name: user-detail
# from UserViewSet.retrieveWith DefaultRouter we have an API root view and allows for an optional style format suffix (e.g. .json
)
See the DRF documentation here for details
You can use DRF to authenticate that you are you.
####Create Token
You can create a token by going into the dbshell.
will = User.objects.create(username='will')
new_token = Token.objects.create(user=will)
print new_token
<Token: fjsdaklfjdsalfdsjaflasjflsa>
Then in Postman, insert in ‘Headers’ the following key and value:
Authorization Token fjsdaklfjdsalfdsjaflasjflsa