Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Django REST framework workshop - DjangoCong 2015

Django REST framework workshop - DjangoCong 2015

xordoquy

May 08, 2015
Tweet

More Decks by xordoquy

Other Decks in Programming

Transcript

  1. API • Application Programming Interface • un ensemble normalisé de

    classes, de méthodes ou de fonctions qui sert de façade par laquelle un logiciel offre des services à d’autres logiciels • Elle est offerte par […] un service web Merci Wikipedia !
  2. REST • REpresentational State Transfer • Style d’architecture pour les

    systèmes hypermedia distribués • Roy Fielding (2000) • Architecture RESTful
  3. REST • Client - Serveur • Sans état • Mise

    en cache • Interface uniforme: • Identification des ressources • Manipulation des ressources • Message autodescritif • Hypermedia • Hiérarchie par couche
  4. DRF Content negotiation / Parser Router Authentication / Permissions /

    Throttling Deserialization Processing / Pagination / Filtering Serialization Renderer Middleware
  5. Création de l’environnement virtuel $ virtualenv workshop_drf_env Using base prefix

    '/opt/local/Python.framework/Versions/3.4' New python executable in workshop_drf_env/bin/python3.4 Also creating executable in workshop_drf_env/bin/python Installing setuptools, pip...done. $ cd workshop_drf_env/ $ source bin/activate (workshop_drf_env) $
  6. Installation des dépendances (workshop_drf_env)$ pip install Django==1.8.1 djangorestframework django-cors-headers Collecting

    Django==1.8.1 Downloading Django-1.8.1-py2.py3-none-any.whl (6.2MB) 100% |████████████████████████████████| 6.2MB 23.2MB/s Collecting djangorestframework==3.1.1 Downloading djangorestframework-3.1.1-py2.py3-none-any.whl (463kB) 100% |████████████████████████████████| 466kB 23.1MB/s Collecting django-cors-headers Downloading django-cors-headers-1.0.0.tar.gz Installing collected packages: Django, djangorestframework, django- cors-headers Running setup.py install for django-cors-headers Successfully installed Django-1.8.1 django-cors-headers-1.0.0 djangorestframework-3.1.1 (workshop_drf_env)$
  7. Création du projet (workshop_drf_env)$ django-admin startproject workshop_drf (workshop_drf_env)$ cd workshop_drf/

    (workshop_drf_env)$ python manage.py startapp todo (workshop_drf_env)$ mv todo workshop_drf/
  8. Disposition workshop_drf + manage.py + workshop_drf + __init__.py + settings.py

    + urls.py + wsgi.py + todo + __init__.py + admin.py + models.py + tests.py + views.py + migrations + __init__.py
  9. settings.py INSTALLED_APPS = ( … # Project 'workshop_drf.todo', # 3rd

    parties 'rest_framework', 'corsheaders', ) MIDDLEWARE_CLASSES = ( … 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', … ) CORS_ORIGIN_ALLOW_ALL = True
  10. todo/models.py from django.db import models from django.conf import settings class

    Category(models.Model): name = models.CharField(max_length=64) def __str__(self): return self.name class Task(models.Model): name = models.CharField(max_length=64) owner = models.ForeignKey(settings.AUTH_USER_MODEL) categories = models.ManyToManyField(Category, related_name="tasks") done = models.BooleanField(default=False) def __str__(self): return self.name
  11. Création des migrations $ python manage.py check System check identified

    no issues (0 silenced). $ python manage.py makemigrations Migrations for 'todo': 0001_initial.py: - Create model Category - Create model Task
  12. Création de la base $ python manage.py migrate Operations to

    perform: Synchronize unmigrated apps: messages, staticfiles, rest_framework Apply all migrations: admin, contenttypes, sessions, todo, auth Synchronizing apps without migrations: Creating tables... Running deferred SQL... Installing custom SQL... Running migrations: Rendering model states... DONE Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK […] Applying sessions.0001_initial... OK Applying todo.0001_initial... OK
  13. todo/serializers.py from rest_framework import serializers from . import models class

    Category(serializers.ModelSerializer): class Meta: model = models.Category class Task(serializers.ModelSerializer): class Meta: model = models.Task
  14. todo/views.py from rest_framework import viewsets from . import serializers, models

    class Category(viewsets.ModelViewSet): queryset = models.Category.objects.all() serializer_class = serializers.Category class Task(viewsets.ModelViewSet): queryset = models.Task.objects.all() serializer_class = serializers.Task
  15. todo/urls.py from django.conf.urls import url, include from rest_framework.routers import DefaultRouter

    from . import views router = DefaultRouter() router.register(r'category', views.Category) router.register(r'task', views.Task) urlpatterns = [ url(r'^', include(router.urls)), url(r'^api-auth/', include( 'rest_framework.urls', namespace='rest_framework')) ]
  16. urls.py from django.conf.urls import include, url from django.contrib import admin

    import workshop_drf.todo.urls urlpatterns = [ url(r'^admin/', include(admin.site.urls)), url(r'', include(workshop_drf.todo.urls)), ]
  17. Ajout des tests from django.core.urlresolvers import reverse from rest_framework import

    status from rest_framework.test import APITestCase from django.contrib.auth import get_user_model from . import models User = get_user_model()
  18. Ajout des tests class TestTask(APITestCase): def test_task_creation(self): user = User.objects.create(username="admin")

    category = models.Category.objects.create(name="Django") url = reverse('task-list') data = { "name": "demo", "owner": user.id, "categories": [category.id], "done": False, } response = self.client.post(url, data, format='json') self.assertEqual( response.status_code, status.HTTP_201_CREATED) data['id'] = 1 self.assertEqual(response.data, data) self.assertEqual(category.tasks.count(), 1)
  19. tests $ python manage.py test Creating test database for alias

    'default'... . ---------------------------------------------------------------------- Ran 1 test in 0.053s OK Destroying test database for alias 'default'... $
  20. représentation améliorée • Ajout d’un control des champs affichés •

    Usage du nom de l’utilisateur • Usage du nom des catégories
  21. todo/serializers.py from rest_framework import serializers from django.contrib.auth import get_user_model from

    . import models class Category(serializers.ModelSerializer): class Meta: model = models.Category fields = ('id', 'name')
  22. todo/serializers.py (suite) class Task(serializers.ModelSerializer): owner = serializers.SlugRelatedField( slug_field='username', queryset=get_user_model().objects.all()) categories

    = serializers.SlugRelatedField( slug_field='name', queryset=models.Category.objects.all(), many=True) class Meta: model = models.Task fields = ('id', 'name', 'owner', 'categories', 'done')
  23. More fun: add links class Task(serializers.ModelSerializer): url = serializers.HyperlinkedIdentityField( 'task-detail',

    source='id', read_only=True) owner = serializers.SlugRelatedField( slug_field='username', queryset=get_user_model().objects.all()) categories = serializers.SlugRelatedField( slug_field='name', queryset=models.Category.objects.all(), many=True) class Meta: model = models.Task fields = ('id', 'name', 'owner', 'categories', 'done', 'url')
  24. Catégories [{ "id": 4, "name": "Django", "tasks": [{ "id": 1,

    "name": "Faire les slides", "owner": "admin", "categories": ["Django"], "done": false }, { "id": 3, "name": "Fix #546", "owner": "admin", "categories": ["Django"], "done": false }] }]
  25. Mes taches: • Toutes mes taches: /task/mine/ • Mes taches

    dans une catégorie: /category/<name>/mine/
  26. from rest_framework.response import Response from rest_framework.decorators import detail_route, list_route […]

    class Task(viewsets.ModelViewSet): queryset = models.Task.objects.all() serializer_class = serializers.Task @list_route() def mine(self, request): queryset = self.filter_queryset( self.get_queryset().filter(owner=request.user)) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) todo/views.py
  27. todo/views.py class Category(viewsets.ModelViewSet): queryset = models.Category.objects.all() serializer_class = serializers.Category @detail_route()

    def mine(self, request, *args, **kwargs): category = self.get_object() category.my_tasks = category.tasks.filter(owner=request.user) serializer = serializers.MyCategory(category) return Response(serializer.data)
  28. todo/filters.py import django_filters from . import models class Task(django_filters.FilterSet): class

    Meta: model = models.Task fields = ['done', 'owner', 'categories']
  29. todo/views.py from rest_framework import viewsets, filters class Task(viewsets.ModelViewSet): queryset =

    models.Task.objects.all() serializer_class = serializers.Task filter_backends = (filters.DjangoFilterBackend,) filter_fields = ('categories', 'owner', 'done')
  30. todo/filters.py import django_filters from django.contrib.auth import get_user_model from . import

    models class Task(django_filters.FilterSet): owner = django_filters.ModelChoiceFilter( to_field_name=« username", queryset=get_user_model().objects.all()) categories = django_filters.ModelMultipleChoiceFilter( to_field_name=« name", queryset=models.Category.objects.all()) class Meta: model = models.Task fields = ['done', 'owner', 'categories']