Introducción a Django Rest Framework

Jesús Espino / @jespinog / http://github.com/jespino

Yo y los servicios web

  • SOAP
  • Tastypie
  • Adhoc
  • Django Rest Framework

SOAP

Tastypie

Adhoc

Django Rest Framework

¿Qué es?

  • Aplicación Django
  • Generador de APIs REST
  • Open Source
  • Creado por Tom Christie a finales del 2010
  • Compatible con python2 y python3
  • Compatible con Django >= 1.3

¿Por qué?

  • API navegable
  • Facil de implementar APIs simples
  • Facil de implementar APIs RESTful
  • Facil de probar
  • Facil de extender o personalizar
  • Muy modular (basado principalmente en Mixins)
  • Muy bien documentado
  • Amplia comunidad de usuarios

API desde mis modelos

Solo son necesarios 3 pasos:

  • Definir mi modelo
  • Definir un viewset (Recurso)
  • Definir y usar un router

Definir mi modelo


class MyModel(models.Model):
    name = models.CharField(max_length=100)
    thing = models.IntegerField()
    internal_thing = models.FloatField(null=True, blank=True)
    owner = models.ForeignKey(User)
                            

Definir un viewset


class MyModelViewSet(viewsets.ModelViewSet):
    model = MyModel
                            

Definir y usar un router


router = DefaultRouter()
router.register('my-model', MyModelViewSet)

urlpatterns = patterns('',
    url(r'^api/v1/', include(router.urls)),
    ...
)
                            

Hi Five!

Principales componentes

  • APIViews
  • ViewSet
  • Routers
  • Parsers
  • Renderers
  • Serializers
  • Autentications
  • Permissions
  • Filters

ApiViews

  • Class Views.
  • Extiende el HTTPRequest.
  • Extiende el HTTPResponse.
  • Captura las excepciones APIException.
  • Procesa la autenticación, permisos y demas de las peticiones.

ViewSets

  • APIViews.
  • Permite definir todas las acciones de un recurso en una sola vista.
  • Utiliza el Router para definir sus urls.
  • Permite definir acciones extra en la API de manera sencilla.

Routers

  • Permite definir las urls de uno o varios ViewSets.

Parsers

  • Define como convertir los datos enviados por el usuario a tipos basicos de python.

Renderers

  • Define como convertir los tipos basicos de python a datos para el usuario.

Serializers

  • Define como convertir datos internos de la aplicacion a los tipos basicos de python (y viceversa).

Authentications

  • Define como saber si una petición esta autenticada.

Permissions

  • Define como saber si una petición esta autorizada.

Filters

  • Filtra los contenidos de un ModelViewSet o un ModelAPIView.

What?

Petición a una APIView

Petición a un ViewSet

Mejorando mi API

Serializador


class MyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyModel
        fields = ('id', 'name', 'thing', 'owner')

class MyModelViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer
                            

O puedes definirlo globalmente en DEFAULT_MODEL_SERIALIZER_CLASS.

Autenticación y permisos


class MyModelViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer
    authentication_classes = (SessionAuthentication, BasicAuthentication)
    permission_classes = (IsAuthenticated,)
                            

O puedes definirlos globalmente en DEFAULT_PERMISSION_CLASSES y DEFAULT_AUTHENTICATION_CLASSES.

Paginación


class MyModelViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer
    authentication_classes = (SessionAuthentication, BasicAuthentication)
    permission_classes = (IsAuthenticated,)
    paginate_by = 10
    paginate_by_param = 'page_size'
    max_paginate_by = 100
                            

O puedes definirlo globalmente en PAGINATE_BY, PAGINATE_BY_PARAM y MAX_PAGINATE_BY.

Filtros


class IsOwnerFilterBackend(filters.BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        return queryset.filter(owner=request.user)

class MyModelViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer
    authentication_classes = (SessionAuthentication, BasicAuthentication)
    permission_classes = (IsAuthenticated,)
    filter_backends = (IsOwnerFilterBackend, filters.OrderingFilter,)
    paginate_by = 10
    paginate_by_param = 'page_size'
    max_paginate_by = 100
                            

O puedes definirlo globalmente en DEFAULT_FILTER_BACKENDS.

ViewSet sin modelo


class NoModelViewSet(viewsets.ViewSet):
    authentication_classes = (SessionAuthentication, BasicAuthentication)
    permission_classes = (IsAuthenticated,)
    def list(self, request):
        return Response([{'hello': 'list'}])
    def retrieve(self, request, pk=None):
        return Response({'hello': pk})
    def create(self, request):
        return Response({'hello': 'create', 'data': request.DATA})
    def update(self, request, pk=None):
        return Response({'hello': pk, 'data': request.DATA})
    def partial_update(self, request, pk=None):
        return Response({'hello': pk, 'data': request.DATA})
    def destroy(self, request, pk=None):
        return Response({'hello': pk})
                            

APIViews sin modelo


class NoModelAPIView(views.APIView):
    authentication_classes = (SessionAuthentication, BasicAuthentication)
    permission_classes = (IsAuthenticated,)

    def get(self, request, format=None):
        return Response([{'hello': 'get'}])

    def post(self, request, format=None):
        return Response([{'hello': 'post'}])

    def put(self, request, format=None):
        return Response([{'hello': 'put'}])

    def delete(self, request, format=None):
        return Response([{'hello': 'delete'}])
                            

Serializer with extra fields


class MyModelSerializer(serializers.ModelSerializer):
    method_field = serializers.SerializerMethodField('get_method_field_data')
    private_data = serializers.Field(source='internal_thing')

    def get_method_field_data(self, obj):
        return "This is a method generated data!"

    class Meta:
        model = MyModel
        fields = ('id', 'name', 'thing', 'owner', 'private_data', 'method_field')
                            

Dudas...