Share
Embed
Transcript
50 slides
-
Workshop • Généralités • Premier jet de l’API • API
plus complête -
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 ! -
REST • REpresentational State Transfer • Style d’architecture pour les
systèmes hypermedia distribués • Roy Fielding (2000) • Architecture RESTful -
REST • Client - Serveur • Sans état • Mise
en cache • Interface uniforme: • Identification des ressources • Manipulation des ressources • Message autodescritif • Hypermedia • Hiérarchie par couche -
Django REST framework • encourage le REST • ne contraint
pas (RPC possible) -
DRF Content negotiation / Parser Router Authentication / Permissions /
Throttling Deserialization Processing / Pagination / Filtering Serialization Renderer Middleware -
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) $ -
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)$ -
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/ -
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 -
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 -
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 -
todo/models.py class Category(models.Model): name = models.CharField(max_length=64) def __str__(self): return self.name
class Meta: verbose_name_plural = "Categories" -
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 -
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 -
todo/admin.py from django.contrib import admin from . import models admin.site.register(models.Category)
admin.site.register(models.Task) -
Play time ! $ python manage.py runserver http://127.0.0.1/admin/ créez quelques
catégories et taches -
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 -
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 -
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')) ] -
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)), ] -
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() -
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) -
tests $ python manage.py test Creating test database for alias
'default'... . ---------------------------------------------------------------------- Ran 1 test in 0.053s OK Destroying test database for alias 'default'... $ -
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 -
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') -
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') -
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') -
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 }] }] -
todo/serializers.py class Category(serializers.ModelSerializer): tasks = Task(many=True, read_only=True) class Meta: model
= models.Category fields = ('id', 'name', 'tasks') -
Mes taches: • Toutes mes taches: /task/mine/ • Mes taches
dans une catégorie: /category/<name>/mine/ -
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 -
todo/serializers.py class MyCategory(serializers.ModelSerializer): tasks = Task(many=True, source='my_tasks') class Meta: model
= models.Category fields = ('id', 'name', 'tasks') -
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) -
todo/filters.py import django_filters from . import models class Task(django_filters.FilterSet): class
Meta: model = models.Task fields = ['done', 'owner', 'categories'] -
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') -
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']