REST in peace
Python
Roadmap
- Python
- Requests
- Django
- Django REST Framework
- REST et OpenAPI
- sécurité des WS
ATTENTION!
Ceci n'est qu'un itinéraire prévu...
Python
Le langage
Objectifs
- Maîtrise suffisante du langage pour les WS
- éviter les slides sur la syntaxe arithmétique
- Montrer les qualités du langage
- donner les sources nécessaires à plus de (auto) formation
Historique
- Created by Guido Van Rossum in 1990
- Promoted himself as BDFL : Benevolent Dictator For Life
- First release of the backward incompatible release 3.0 in 2008
- Last release of the 2.7 version in 2010
(Mais très court! Promis...)
Version
La version du langage utilisé
(Aujourd'hui, nous utilisons la version 3.10)
Interpréteur
L'implémentation du langage.
Il en existe plusieurs:
- CPython
- IronPython
- PyPy
- Jython
- ...
Distribution
Certaines librairies sont difficiles à installer sous Windows.
Une distribution comme Anaconda permet de gagner du temps.
Anatomie
- Multiparadigme
- impératif, fonctionnel, orienté objet, orienté aspect
- Interprété (OS Portable)
- Typage Dynamique
- Garbage collector
- Lisible...
Sinon, c'est mieux perçu par la pratique...
L'interpréteur
- python.exe
- utilisation du -m
- gestion des variables / objets
- utilisation de "_"
- pas besoin de "print()"
Types primitifs
- typage dynamique mais fort!: type()
- int, float, bool, None
- str (+raw)
- print()
- [] omniprésent!
- bytes
- decode()
NB: TOUT est objet en Python
Les listes
- instanciation
- indexation
- slices (get,set), copy
- négatifs, steps
- compréhension
- méthodes
- append(), sort[ed]()
- opérateurs
- in
- +,*,...
- idiome: range(len()), enumerate()
Les dictionnaires
- instanciation
- indexation
- compréhension
- méthodes
- .items(), .setdefault()
- opérateurs
- in, |, ...
- idiome: dict(zip())
Consonnes / voyelles
Les tuples et les sets
- instanciation
- indexation
- compréhension
- comparaison avec list et dict...
utilisation de "for" pour tous
Python est basé sur les "protocoles" => duck-typing
Les fonctions
def say_hello():
"""
documentation optionnelle (donc indispensable)
appelée 'docstring'.
"""
return "Hello world!"
print(say_hello())
Déclaration
- snake-case
- retourne "None" par défaut.
- "pass"
Scope
(visibilité des variables)
l'appel d'une fonction génère une nouvelle table des symboles contenant:
- symboles "built-in"
- variables globales (du module)
- variables des scopes supérieurs
- (une fonction peut être définie dans une autre => closure)
- paramètres de la fonction
- variables locales
Paramètres
déclarations dans l'ordre:
- paramètres positionnels obligatoires
- paramètres positionnels optionnels
- (avec valeur par défaut)
- paramètres positionnels additionnels
- *args
- paramètres nommés additionnels
- **kwargs
Lors de l'appel => list+dict unpacking
Exercice product()
Ecrire une fonction de multiplication vérifiant:
-
multiply([8, 2, 3, -1, 7]) == -336
-
multiply([8, 2, 3, -1, 7]) == multiply(8, 2, 3, -1, 7)
open()
- encoding
- str <=> bytes
- module io
- utilisation de "with"
private String open(String path) throws IOException {
InputStream inputStream = null;
try {
File file = new File(classLoader.getResource(path).getFile());
inputStream = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = br.readLine()) != null) {
resultStringBuilder.append(line).append("\n");
}
return resultStringBuilder.toString();
}
finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Les lambdas
Très utiles comme paramètres d'autres fonctions:
- sorted
- filter
- map (préférez une compréhension)
- reduce (désormais dans functools)
pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
sorted(pairs, key=lambda pair: pair[1])
Les exceptions
- sont des objets...
- try/except/finally
- with
- (context managers)
- "gérées le plus tard possible."
Les modules
- "batteries included"
- math, os, sys, logging, argparse, ...
- package manager (pip)
- import, from, as
- packages
- __init__.py
- "__main__" + sys.argv
- sont des objets (attributs dynamiques).
- mais le "monkey patching" est un grand pouvoir qui implique...
Exercice "prime"
Ecrire un module de manipulation des nb premiers:
- chargement d'une liste de nombres premiers (depuis un fichier)
- écriture d'un fichier au même format (csv ou autre)
- recherche de nombres premiers depuis le dernier de la liste
POO
(mais pas trop)
Les classes
class Bird:
def __init__(self, mood="Happy", size=0):
self.mood = mood
self.size = size
def fly(self):
self.mood = "Happy"
print("Look at me! I'm flying!")
def kick(self):
self.mood = "Angry"
Les définitions de classes sont exécutées pour produire une classe
(qui est un objet...)
Les classes
woody = Bird(mood="neutral")
type(woody) == Bird # True
Les classes sont appelées pour créer des instances.
Ca ressemble pas mal à des fonctions...
Héritage
class Duck(Bird):
def quack(self):
print "Quack, quack!"
def kick(self):
self.quack()
super(Duck, self).kick()
daffy = Duck()
type(daffy) == Duck
# Polymorphism
isinstance(daffy, Bird) == True
daffy.fly()
Héritage
# Let's adopt another duck:
donald = Duck(size=10)
# How is it linked to daffy?
donald.nephews = ["Huey", "Dewey", "Louie"]
daffy.nephews #NameError
donald.__class__.face = "Duck-billed"
donald.face # "Duck-billed"
daffy.face # "Duck-billed"!!!
# How is it linked to woody?
woody.face # NameError
woody.__class__.personality = "funny"
woody.personality # "funny"
donald.personality # "funny"
# What happens if I do:
daffy.personality = "evil" #???
Duck-typing
class Goose(Bird): # A Goose is NOT a Duck
def quack(self):
print("Quack like a Goose")
def make_it_quack(o):
o.quack()
sauvage = Goose()
make_it_quack(sauvage)
make_it_quack(donald) #It works!
- Duck-typing := pas de vérification de type
- => définition de "protocoles" plutôt que d'interfaces
Méthodes magiques
des protocoles standards, définis par le langage.
Exercice magique
- Choisissez un thème:
- fruits, formes géométriques, véhicules…
- définir ce que signifie d'itérer, d'ajouter, etc... des objets de ce type
c'est magique aussi parce que ça marche avec tout…
Et la performance!
On en parle?
Ce que nous ne verrons pas cette fois...
- générateurs
- décorateurs
- context managers
- meta classes
- coroutines
- typage statique
- descripteurs
- toute la lib standard
concepts
- Dataclasses
-
async def, with, for + await
- __await__ -> awaitable (returns an iterable)
- async def -> coroutine (are awaitable!)
- __aiter__ -> async iterable (return async iterator)
- __anext__ -> async iterator (returns awaitable)
- __aenter/exit__-> async ctxt mng (return awaitables)
- positional , / , pos-or-kw , * , kw
- single dispatch
- slice: [:] -> slice obj in __getitem__
-
type hints
- Generic[], TypeVar()
Requests
(HTTP for humans)
#pip install requests
response = requests.get(
"http://www.google.fr",
proxies={"http": "http://192.168.3.3:8080"},
)
# OU (avec cookies jar)
session = requests.Session()
session.proxies={"http": "http://192.168.3.3:8080"}
response = session.get("http://www.google.fr")
#pip install beautifulsoup4
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.content)
soup.find_all("a")
Django
Un framework pour les gouverner tous
Les concepts
Possède des modules pour gérer:
- projets / applications
- vues / URLs
- modèles / migrations
- administration (backoffice)
- tests
- templates
- formulaires
- ...
+ Sécurité par défaut
Installation
# creation environement virtuel
python -m venv .env
. .env/Scripts/activate
# installation
pip install Django
# Vérification
python -m django --version
Vues
# création projet
django-admin startproject demodj .
# création app
python manage.py startapp demo
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world!")
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
# add to server's urls.py:
# path('demo/', include('demo.urls')),
Modèles, ORM, Migrations
python manage.py startapp drvapp
# register in settings.py
# 'default': {
# 'ENGINE': 'django.db.backends.mysql',
# 'NAME': 'DRVAPP_CENTRAL_DATAS',
# 'USER': 'inspect',
# 'PASSWORD': '1nspect',
# 'HOST': '10.34.2.8',
# 'PORT': '3306',
# },
python manage.py inspectdb > drvapp/models.py
# edit generated models.py
python -Xutf8 manage.py dumpdata drvapp -o data.json
# back to default db
python manage.py makemigrations
python manage.py migrate
python manage.py loaddata data.json
python manage.py shell
Admin (backoffice)
from .models import Tournee, Point, Colis
admin.site.register(Tournee)
admin.site.register(Point)
admin.site.register(Colis)
# Et beaucoup plus:
# https://docs.djangoproject.com/en/4.0/intro/tutorial07/
python manage.py createsuperuser
Vues (classes)
from django.views import generic
class IndexView(generic.ListView):
template_name = 'wsdrvapp/index.html'
def get_queryset(self):
return Tournee.objects.order_by('-damj')[:5]
class DetailView(generic.DetailView):
model = Tournee
template_name = 'wsdrvapp/detail.html'
app_name = "wsdrvapp"
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
]
Django REST Framework
REST
(Representational State Transfer)
Contraintes
- client/serveur
- sans état
- on transfère une représentation de la ressource à un instant donné
- identification des ressources
- HATEOAS (navigation)
Avantages
- découplage
- réactivité
- standardisation
- accessibilité (HTTP)
- scalabilité horizontale
Les concepts DRF
Possède des modules pour gérer:
- parsing / rendering
- serializers / validators
- viewsets
- routeurs
- authentication
- permissions
- pagination
- filtering
- throttling...
Installation
# Dans un projet django
pip install djangorestframework
pip install markdown # Markdown support for the browsable API.
pip install django-filter # Filtering support
# 'rest_framework' dans INSTALLED_APPS
# settings specifiques dans REST_FRAMEWORK = {}
# path('api-auth/', include('rest_framework.urls'))
Serializers
from rest_framework import serializers
from .models import Tournee
class TourneeSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Tournee
fields = '__all__'
from wsdrvapp.models import Tournee
from wsdrvapp.serializers import TourneeSerializer
from pprint import pprint
t = Tournee.objects.first()
s = TourneeSerializer(t, context={'request':None})
pprint(s.data)
Viewsets
from rest_framework import viewsets
from .models import Point, Tournee
from .serializers import TourneeSerializer
class TourneeViewSet(viewsets.ModelViewSet):
queryset = Tournee.objects.all()
serializer_class = TourneeSerializer
Router
from wsdrvapp.views import TourneeViewSet
from rest_framework import routers
router = routers.DefaultRouter()
router.register('tournee', TourneeViewSet)
urlpatterns = [
# ...
path('api/', include(router.urls)),
]
Permissions
(authentication)
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticatedOrReadOnly',
],
}
from rest_framework import schemas
urlpatterns = [
# ...
path('api-auth/', include('rest_framework.urls')),
]
Pagination
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 5,
}
Filtering (1/2)
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend',
'rest_framework.filters.SearchFilter',
'rest_framework.filters.OrderingFilter',
),
}
class TourneeViewSet(viewsets.ModelViewSet):
queryset = Tournee.objects.all()
serializer_class = TourneeSerializer
filterset_fields = ('num', 'bl')
search_fields = ['num', 'date', 'bl', 'loccode']
Filtering (2/2)
class PointViewSet(viewsets.ModelViewSet):
queryset = Point.objects.all()
serializer_class = PointSerializer
filterset_class = PointFilter
search_fields = ['cp', 'tournee__num']
from django_filters import rest_framework as filters
from .models import Point
class PointFilter(filters.FilterSet):
min_lat = filters.NumberFilter(field_name="lat", lookup_expr='gte')
max_lat = filters.NumberFilter(field_name="lat", lookup_expr='lte')
min_lon = filters.NumberFilter(field_name="lon", lookup_expr='gte')
max_lon = filters.NumberFilter(field_name="lon", lookup_expr='lte')
class Meta:
model = Point
fields = ['cp']
OpenAPI / Schemas
from rest_framework import schemas
urlpatterns = [
# ...
path('openapi', schemas.get_schema_view(
title="WSDriverApp",
description="API for all things …",
version="1.0.0"
), name='openapi-schema'),
]
Swagger UI
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('api-auth/', include('rest_framework.urls')),
path('api/', include(router.urls)),
path('openapi', schemas.get_schema_view(
title="WSDriverApp",
description="API for all things …",
version="1.0.0"
), name='openapi-schema'),
path('swagger-ui', TemplateView.as_view(
template_name='wsdrvapp/swagger-ui.html',
extra_context={'schema_url':'openapi-schema'}
), name='swagger-ui'),
]
+ un template
Throttling
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '5/min',
'user': '1000/day'
}
}
Parsing / Rendering
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser',
'rest_framework_yaml.parsers.YAMLParser',
],
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
'rest_framework_yaml.renderers.YAMLRenderer',
],
}
pip install djangorestframework-yaml
Merci ^^
Merci ^^
Python
By Olivier DUPOUY
Python
a generic but complete introduction to python programming
- 74