
Andrea Stagi
Develover @
Nephila
 
Open source Framework to build your own WEB API
github.com/tomchristie/django-rest-framework

The app
https://github.com/DjangoBeer
from django.db import models
from django.contrib.auth.models import User
class CoffeeUser(models.Model):
    user = models.OneToOneField(User)
    twitter = models.CharField(max_length=30, default='')
class Badge(models.Model):
    title = models.CharField(max_length=100)
    description = models.CharField(max_length=100)
class Consumption(models.Model):
    user = models.ForeignKey(User, related_name='consumptions')
    created = models.DateTimeField(auto_now_add=True)
class PoweredBadge(models.Model):
    user = models.ForeignKey(User)
    badge = models.ForeignKey(Badge)
    power = models.PositiveIntegerField(default=1)
Serialization
from .models import Badge, Consumption, PoweredBadge
from django.contrib.auth.models import User
from rest_framework import serializers
class PoweredBadgeSerializer(serializers.ModelSerializer):
    title = serializers.CharField(source='badge.title', read_only=True)
    description = serializers.CharField(source='badge.description', read_only=True)
    class Meta:
        model = PoweredBadge
        fields = ('id', 'title', 'description', 'power')
class BadgeSerializer(serializers.ModelSerializer):
    class Meta:
        model = Badge
        fields = ('id', 'title', 'description')
# To be continued..
# ....
class ConsumptionSerializer(serializers.ModelSerializer):
    user = serializers.IntegerField(source='user.pk', read_only=True)
    class Meta:
        model = Consumption
        fields = ('id', 'user', 'created')
class UserSerializer(serializers.ModelSerializer):
    badges = PoweredBadgeSerializer(source='poweredbadge_set', many=True)
    twitter = serializers.CharField(source='coffeeuser.twitter')
    class Meta:
        model = User
        fields = ('id', 'username', 'badges', 'twitter')Custom Serializers
class StatisticSerializer(serializers.BaseSerializer):
    def to_representation(self, obj):
        user_data = {}
        user_data['user'] = UserSerializer(obj).data
        user_data['consumptions'] = [
            ConsumptionSerializer(consumption).data for consumption in obj.consumptions.all()
        ]
        return user_dataViews
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Badge
from .serializers import BadgeSerializer
class BadgeList(APIView):
    """
    List all badges or create a new badge.
    """
    def get(self, request, format=None):
        badges = Badge.objects.all()
        serializer = BadgeSerializer(badges, many=True)
        return Response(serializer.data)
    def post(self, request, format=None):
        serializer = BadgeSerializer(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)class BadgeDetail(APIView):
    """
    Retrieve, update or delete a badge instance.
    """
    def get_object(self, pk):
        try:
            return Badge.objects.get(pk=pk)
        except Badge.DoesNotExist:
            raise Http404
    def get(self, request, pk, format=None):
        badge = self.get_object(pk)
        serializer = BadgeSerializer(badge)
        return Response(serializer.data)
    def put(self, request, pk, format=None):
        badge = self.get_object(pk)
        serializer = BadgeSerializer(badge, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    def patch(self, request, pk, format=None):
        badge = self.get_object(pk)
        serializer = BadgeSerializer(badge, data=request.data, partial=True)
        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):
        badge = self.get_object(pk)
        badge.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)from django.conf.urls import patterns, url
from rest_framework.urlpatterns import format_suffix_patterns
from .views import BadgeDetail, BadgeList
urlpatterns = [
    url(r'^badges/$', BadgeList.as_view()),
    url(r'^badges/(?P<pk>[0-9]+)$', BadgeDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)Authentication
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    )
}
 Token Authentication
INSTALLED_APPS = (
    ...
    'rest_framework.authtoken'
)from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
for user in User.objects.all():
    Token.objects.get_or_create(user=user)from rest_framework.authtoken import views
urlpatterns += [
    url(r'^api-token-auth/', views.obtain_auth_token)
]from rest_framework.authtoken import views
urlpatterns += [
    url(r'^api-token-auth/', views.obtain_auth_token)
]Authorization: Token 9944b09199c62bcf9
{ 'token' : '9944b09199c62bcf9' }
Django OAuth Toolkit
DRF OAUTH
JSON WEB TOKEN AUTH
MORE @ http://bit.ly/1yt2Ign
Custom authentication
from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions
class DeviceAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        
        ...Check X_DEVICE header, return None if it's not set
Check if Device is allowed and return it
Otherwise raise AuthenticationFailed
permissions
from rest_framework import permissions
class BlacklistPermission(permissions.BasePermission):
    """
    Global permission check for blacklisted IPs.
    """
    def has_permission(self, request, view):
        ip_addr = request.META['REMOTE_ADDR']
        blacklisted = Blacklist.objects.filter(ip_addr=ip_addr).exists()
        return not blacklistedObject Permissions
.check_object_permissions(request, obj) class BadgeDetail(APIView):
    def get(self, request, pk, format=None):
        badge = self.get_object(pk)
        self.check_object_permissions(request, badge)
        serializer = BadgeSerializer(badge)
        return Response(serializer.data)
    def put(self, request, pk, format=None):
        badge = self.get_object(pk)
        self.check_object_permissions(request, badge)
        serializer = BadgeSerializer(
            badge, data=request.data
        )
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(
            serializer.errors, 
            status=status.HTTP_400_BAD_REQUEST
        )
    def patch(self, request, pk, format=None):
        badge = self.get_object(pk)
        self.check_object_permissions(request, badge)
        serializer = BadgeSerializer(
            badge, 
            data=request.data, partial=True
        )
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(
            serializer.errors, 
            status=status.HTTP_400_BAD_REQUEST
        )
    #...If you're writing your own views and want to enforce object level permissions, or if you override the get_object method on a generic view, then you'll need to explicitly call the
method
on the view at the point at which you've retrieved the object.
Custom permissions
from rest_framework import permissions
class IsSuperUserOrReadOnly(permissions.BasePermission):
    """
    Object-level permission to only allow superusers to edit objects.
    """
    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True
        # You need to be superuser to do POST, PUT and PATCH requests.
        return request.user.is_superuserclass IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Custom permission to only allow owners of an object to edit it.
    """
    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True
        # Write permissions are only allowed to the owner of the object.
        return obj.user == request.user
Mixins
from rest_framework import mixins
from rest_framework import generics
class BadgeList(mixins.ListModelMixin,
                mixins.CreateModelMixin,
                generics.GenericAPIView):
    """
    List all badges or create a new badge.
    """
    queryset = Badge.objects.all()
    serializer_class = BadgeSerializer
    permission_classes = (IsSuperUserOrReadOnly,)
    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 BadgeDetail(mixins.RetrieveModelMixin,
                  mixins.UpdateModelMixin,
                  mixins.DestroyModelMixin,
                  generics.GenericAPIView):
    """
    Retrieve, update or delete a badge instance.
    """
    queryset = Badge.objects.all()
    serializer_class = BadgeSerializer
    permission_classes = (IsSuperUserOrReadOnly,)
    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 patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)Generic Class Based Views
from rest_framework import permissions
from rest_framework import generics
class BadgeList(generics.ListCreateAPIView):
    """
    List all badges or create a new badge.
    """
    queryset = Badge.objects.all()
    serializer_class = BadgeSerializer
    permission_classes = (IsSuperUserOrReadOnly,)
class BadgeDetail(generics.RetrieveUpdateDestroyAPIView):
    """
    Retrieve, update or delete a badge instance.
    """
    queryset = Badge.objects.all()
    serializer_class = BadgeSerializer
    permission_classes = (IsSuperUserOrReadOnly,)class ListCreateAPIView(mixins.ListModelMixin,
                        mixins.CreateModelMixin,
                        GenericAPIView):
    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 RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
                                   mixins.UpdateModelMixin,
                                   mixins.DestroyModelMixin,
                                   GenericAPIView):
    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 patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)rest_framework/generics.py
ViewSets
from rest_framework import viewsets
from rest_framework.decorators import list_route
from rest_framework.response import Response
class BadgeViewSet(viewsets.ModelViewSet):
    queryset = Badge.objects.all()
    serializer_class = BadgeSerializer
    permission_classes = (IsSuperUserOrReadOnly,)class ConsumptionViewSet(viewsets.ModelViewSet):
    serializer_class = ConsumptionSerializer
    permission_classes = (permissions.IsAuthenticated,)
    @list_route(methods=['get'], permission_classes=[permissions.AllowAny])
    def all(self, request):
        consumptions = Consumption.objects.all()
        serializer = ConsumptionSerializer(consumptions, many=True)
        return Response(serializer.data)
    def perform_create(self, serializer):
        serializer.save(user=self.request.user)
    def get_queryset(self):
        return Consumption.objects.filter(user=self.request.user)DRF in the real life
@Nephila
Pagination
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 100
}HTTP 200 OK
{
    "count": 1023
    "next": "https://localhost:8000/pubs/?page=4",
    "previous": "https://localhost:8000/pubs/?page=2",
    "results": [
       …
    ]
}GET https://localhost:8000/pubs/?page=3
@Taiga.io
https://github.com/taigaio/taiga-back
class ConditionalPaginationMixin(object):
    def get_paginate_by(self, *args, **kwargs):
        if "HTTP_X_DISABLE_PAGINATION" in self.request.META:
            return None
        return super().get_paginate_by(*args, **kwargs)class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    pass
class ModelCrudViewSet(pagination.HeadersPaginationMixin,
                       pagination.ConditionalPaginationMixin,
                       ModelViewSet):
    passclass ProjectViewSet(ModelCrudViewSet):
    serializer_class = serializers.ProjectDetailSerializer
    ...
    @list_route(methods=["GET"])
    def by_slug(self, request):
        slug = request.QUERY_PARAMS.get("slug", None)
        project = get_object_or_404(models.Project, slug=slug)
        return self.retrieve(request, pk=project.pk)
        
    @detail_route(methods=["GET"])
    def stats(self, request, pk=None):
        project = self.get_object()
        self.check_permissions(request, "stats", project)
        return response.Ok(services.get_stats_for_project(project))
    @detail_route(methods=["POST"])
    def star(self, request, pk=None):
        project = self.get_object()
        self.check_permissions(request, "star", project)
        votes_service.add_vote(project, user=request.user)
        return response.Ok()
    @detail_route(methods=["POST"])
    def unstar(self, request, pk=None):
        project = self.get_object()
        self.check_permissions(request, "unstar", project)
        votes_service.remove_vote(project, user=request.user)
        return response.Ok()SWAPI

https://github.com/phalt/swapi
Pokèapi

https://github.com/phalt/pokeapi
Thank you!
github.com/astagi
Twitter: @4stagi

STAGI.ANDREA@GMAIL.COM
DjangoBeer Django REST Framework
By Andrea Stagi
DjangoBeer Django REST Framework
- 2,518