Django : Proofreading.

This commit is contained in:
Julien Palard 2023-12-17 22:58:22 +01:00
parent 7341368eba
commit 5e69beabe4
Signed by: mdk
GPG Key ID: 0EFC1AC1006886F8
1 changed files with 119 additions and 106 deletions

View File

@ -6,23 +6,26 @@ Julien Palard <julien@palard.fr>
https://mdk.fr
::: notes
notes:
Introduce yourself!
# Django
Django est une infrastructure d'applications web populaire et robuste.
Django est une infrastructure dapplications web populaire et robuste.
> The web framework for perfectionists with deadlines.
## Django : qui l'utilise ?
## Django : qui lutilise ?
- Instagram, Pineterest, Mozilla, Disqus, BitBucket, …
![](static/stats.png)
- Instagram
- Pineterest
- Mozilla
- National Geographic
- [Washington Post](https://www.djangoproject.com/weblog/2005/dec/08/congvotes/)
- ...
## Vocabulaire
@ -55,18 +58,18 @@ Une fois dans le projet, pour créer une application, une commande :
python manage.py startapp watch
```
::: notes
notes:
(et ajout dans `settings.py`)
## La théorie — Modèle
Un « modèle » est la description d'une table.
Un « modèle » est la description dune table.
Ça rappelle un ORM, mais ça permet beaucoup plus de choses en Django.
::: notes
notes:
- admin
- forms
@ -85,14 +88,14 @@ class Website(models.Model):
last_check = models.DateTimeField(auto_now_add=True)
```
::: notes
notes:
Prendre le temps d'expliquer les fields (leur relation avec la DB), et
Prendre le temps dexpliquer les fields (leur relation avec la DB), et
les differents endroits ou Django peut reutiliser cette information
(widgets, validation, ...).
## Première interface d'admin
## Première interface dadmin
En une ligne, pourquoi pas :
@ -107,14 +110,14 @@ admin.site.register(Website)
- sqlite
- ...
::: notes
notes:
Leur faire croire 2 secondes qu'on va devoir s'installer et se
Leur faire croire 2 secondes quon va devoir sinstaller et se
configurer un serveur de base de donnée :D
Leur expliquer que sqlite est utilisé dans les applications : pas
besoin d'installer un postgresql pour utiliser Firefox, pourtant
Firefox a besoin d'une base de donnée.
besoin dinstaller un postgresql pour utiliser Firefox, pourtant
Firefox a besoin dune base de donnée.
## La théorie — La DB
@ -124,15 +127,15 @@ python manage.py makemigrations
python manage.py migrate
```
::: notes
notes:
On expliquera plus tard, leur dire que ça crée la DB et que le but
maintenant c'est surtout d'aller tester ça :)
maintenant cest surtout daller tester ça :)
## L'interface d'administration
## Linterface dadministration
On a une DB, mais pas encore d'utilisateur admin dedans :
On a une DB, mais pas encore dutilisateur admin dedans :
```bash
python manage.py createsuperuser
@ -154,15 +157,15 @@ cd project
python manage.py startapp watch
```
::: notes
notes:
Biiien prendre le temps d'expliquer l'arborescence, de se promener,
d'y lire les commentaires.
Biiien prendre le temps dexpliquer larborescence, de se promener,
dy lire les commentaires.
## La pratique
Ajout de l'app `watch` dans `project/settings.py` :
Ajout de lapp `watch` dans `project/settings.py` :
```python
INSTALLED_APPS = [
@ -185,7 +188,7 @@ class Website(models.Model):
```
## Première interface d'admin
## Première interface dadmin
Et ça dans `watch/admin.py`.
@ -204,19 +207,19 @@ python manage.py makemigrations
python manage.py migrate
```
::: notes
notes:
Expliquer les deux étapes.
## Les modèles
Désambiguons `makemigrations` et `migrate` d'abord.
Désambiguons `makemigrations` et `migrate` dabord.
## L'interface d'administration
## Linterface dadministration
On a une DB, mais pas encore d'utilisateur admin dedans :
On a une DB, mais pas encore dutilisateur admin dedans :
```bash
python manage.py createsuperuser
@ -224,31 +227,31 @@ python manage.py createsuperuser
## Terminé
On a terminé, on peut essayer maintenant ?
On a terminé, on peut essayer maintenant ?
```bash
python manage.py runserver
```
::: notes
notes:
Leur faire faire ça dans un **autre** shell.
puis les laisser jouer avec l'interface d'admin, créer quelques sites...
puis les laisser jouer avec linterface dadmin, créer quelques sites...
## L'interface d'administration
## Linterface dadministration
Les modèles, leurs `fields` ne servent donc pas qu'a l'ORM, cette
interface d'admin nous à demandé une ligne de code.
Les modèles, leurs `fields` ne servent donc pas qua lORM, cette
interface dadmin nous a demandé une ligne de code.
::: notes
notes:
Si ce n'est pas déjà fait, leur faire ajouter des `__str__`.
Si ce nest pas déjà fait, leur faire ajouter des `__str__`.
## Astuce
On peut passer beaucoup de temps à peaufiner l'interface d'admin,
On peut passer beaucoup de temps à peaufiner linterface dadmin,
repoussez ça après avoir livré une première version.
@ -259,28 +262,28 @@ repoussez ça après avoir livré une première version.
Créez le modèle `Check` avec les champs `is_up`, `date`, `website`, et
`message`.
::: notes
notes:
Pour le champ `website` vous aurez besoin d'un `models.ForeignKey`, RTFM.
Pour le champ `website` vous aurez besoin dun `models.ForeignKey`, RTFM.
## L'admin
## Ladmin
Ajoutez une interface d'admin pour ce modèle, et ajoutez à la main quelques « *checks* ».
Ajoutez une interface dadmin pour ce modèle, et ajoutez à la main quelques « *checks* ».
::: notes
notes:
Les faire tester ça.
## Personalisons
Dans chaque modèle, un `__str__` aide l'admin à être lisible.
Dans chaque modèle, un `__str__` aide ladmin à être lisible.
## Personalisons
Dans `admin.py` on peut préciser les colonnes qu'on veut afficher :
Dans `admin.py` on peut préciser les colonnes quon veut afficher :
```python
@admin.register(Website)
@ -296,12 +299,12 @@ class CheckAdmin(admin.ModelAdmin):
# Les URLs & les vues
Changons complètement de sujet : les URLs et des vues.
Changons complètement de sujet : les URLs et des vues.
## Les URLs
Dans `project/urls.py` on va se rajouter une URL pour la page d'accueil :
Dans `project/urls.py` on va se rajouter une URL pour la page daccueil :
```python
from watch import views
@ -313,9 +316,9 @@ urlpatterns = [
]
```
::: notes
notes:
C'est un `path`, un chemin, c'est le chemin vide.
Cest un `path`, un chemin, cest le chemin vide.
## Les URLs
@ -341,18 +344,18 @@ from django.urls import include
path("", include("watch.urls")),
```
::: notes
notes:
C'est pratique pour « ancrer » un ensemble de chemin sous un autre
chemin : pour se faire une hierarchie.
Cest pratique pour « ancrer » un ensemble de chemin sous un autre
chemin : pour se faire une hierarchie.
## namespaces
Les espaces de nommage permettent de désambiguer les urls nommées :
`index` est le nom de la page d'accueil de l'interface d'admin de la
page d'accueil de votre application...
`index` est le nom de la page daccueil de linterface dadmin ou de la
page daccueil de votre application ?
## namespaces
@ -364,9 +367,9 @@ Avec les espaces de nommage, on a donc :
sans ambiguité.
::: notes
notes:
Utiliez-en, c'est bien.
Utiliez-en, cest bien.
## Les vues
@ -383,9 +386,9 @@ def index(request):
```
::: notes
notes:
C'est bien mais écrire du HTML dans du Python c'est pas élégant.
Cest bien mais écrire du HTML dans du Python cest pas élégant.
## Les vues
@ -425,7 +428,7 @@ Donc dans `watch/templates/watch/index.html` :
</html>
```
::: notes
notes:
La création du dossier `templates/` est typiquement quelque chose que
`runserver` ne voit pas, il faut le redémarrer.
@ -433,7 +436,7 @@ La création du dossier `templates/` est typiquement quelque chose que
## Les vues
Et si on ajoutais de la données provenant de la DB dans le template ?
Et si on ajoutait de la donnée provenant de la DB dans le template ?
## Les vues
@ -447,18 +450,18 @@ def index(request):
{"websites": Website.objects.all()})
```
::: notes
notes:
Ne pas oublier les imports…
Premier apperçu de l'ORM en passant.
Premier apperçu de lORM en passant.
## Les vues
Digression :
Il existe aussi des vues basées sur des classes, pouvant s'appuyer sur
Il existe aussi des vues basées sur des classes, pouvant sappuyer sur
des modèles.
@ -485,7 +488,7 @@ class WebsiteListView(ListView):
## Les templates
Ça fonctionne, mais on ne veux pas répéter l'entête HTML à chaque
Ça fonctionne, mais on ne veut pas répéter lentête HTML à chaque
page…
@ -505,14 +508,14 @@ En utilisant `extends`, on peut réutiliser des templates.
python -m pip install django-debug-toolbar
```
L'ajouter dans `settings.py` et `urls.py`.
Lajouter dans `settings.py` et `urls.py`.
# L'ORM
# LORM
## L'ORM
## LORM
C'est l'occasion de sortir un `python manage.py shell`.
Cest loccasion de sortir un `python manage.py shell`.
```pycon
>>> from watch.models import Website
@ -524,8 +527,8 @@ Essayer `.all`, `.filter`, `.get`, `.order_by`, et les slices.
## Les *Managers*
Les *managers* représentent une table, il sont accessible via
l'attribut `objects` d'un modèle.
Les *managers* représentent une table, ils sont accessibles via
lattribut `objects` dun modèle.
Ses opérations (des méthodes) renvoient des `queryset`s.
@ -543,7 +546,7 @@ Les instances de modèles représentent une ligne de la table.
## Les *Queryset*
Représentent un ensemble de lignes de la base de donnée. Ils ont les
mêmes méthodes que les *managers* :
mêmes méthodes que les *managers* :
- filter
- get
@ -561,8 +564,8 @@ Out[3]: <QuerySet [<Website: mdk.fr>]>
## Les *Queryset*
Pour ceux qui ont fait du SQL c'est un "lazy select" : c'est un
`SELECT` qui ne s'éxécutera que si nécessaire.
Pour ceux qui ont fait du SQL cest un "lazy select" : cest un
`SELECT` qui ne séxécutera que si nécessaire.
## Les *RelatedManager*
@ -577,7 +580,7 @@ Out[4]: <QuerySet [<Check: mdk.fr is up>, <Check: mdk.fr is up>]>
# Forms
Comme pour l'admin, Django peut utiliser les informations des modèles
Comme pour ladmin, Django peut utiliser les informations des modèles
pour vous aider à générer des formulaires.
@ -587,7 +590,7 @@ Créer un formulaire ressemble à créer un modèle, on décrit les champs :
```python
class WebsiteForm(forms.Form):
host = forms.CharField(label='Website hostname',
host = forms.CharField(label="Website hostname",
max_length=512)
```
@ -610,7 +613,7 @@ On le donne au template :
## Forms
On l'affiche dans le template :
On laffiche dans le template :
```html
<form action="/" method="post">
@ -639,7 +642,7 @@ class WebsiteForm(forms.ModelForm):
## Widgets
Le rendu HTML d'un champ de formulaire est appelé un `Widget`, il est
Le rendu HTML dun champ de formulaire est appelé un `Widget`, il est
possible de le changer, par exemple si vous préférez un `<input` à un
`<textarea` ou vice-versa.
@ -655,7 +658,7 @@ if request.method == "POST":
Website.objects.create(host=form.cleaned_data["host"])
```
::: notes
notes:
Django peut donc vérifier que les champs non-empty sont bien pas
vides, que les longuers sont respectées, etc…
@ -663,12 +666,12 @@ vides, que les longuers sont respectées, etc…
# Les tests
Tester c'est douter.
Tester cest douter.
::: notes
notes:
Ou pas. Avoir les tests qui passent avant de pousser, avant de merger
une PR, avant de mettre en prod c'est un véritable confort.
une PR, avant de mettre en prod cest un véritable confort.
## Les fixtures
@ -680,21 +683,22 @@ On appelera ça des « fixtures ».
## Les fixtures
Le moyen simple de créer des fixtures est d'utiliser les données que
vous avez crées via l'admin :
Le moyen simple de créer des fixtures est dutiliser les données que
vous avez crée via ladmin :
```bash
./manage.py dumpdata -o watch/fixtures/initial.json
```
::: notes
notes:
Créez le dossier d'abord ;)
Créez le dossier dabord ;)
## Les fixtures
Profitez-en pour indiquer aux collègues dans le README qu'ils peuvent les charger aussi :
Profitez-en pour indiquer aux collègues dans le README quils peuvent
les charger aussi :
```bash
git clone …
@ -710,16 +714,16 @@ Par défaut Django utilise la bibliothèque native `unittest`, on
placera les tests de nos applications dans le dossier `tests` ou le
fichier `tests.py` des applications.
::: notes
notes:
Habituellement on dit que le nom du fichier n'a aucune importance, que
seul le succès de l'import compte. Ici c'est le contraire, le nom du
Habituellement on dit que le nom du fichier na aucune importance, que
seul le succès de limport compte. Ici cest le contraire, le nom du
fichier doit commencer par `test` pour être trouvé.
## Les tests
Les tests s'exécutent via :
Les tests sexécutent via :
```bash
python manage.py test
@ -743,7 +747,7 @@ class WatchTestCase(TestCase):
## Les tests
Pour vérifier que tout se passe bien `unittest` et `Django` nous
fournissent une collection d'assertions :
fournissent une collection dassertions :
- assertTemplateUsed
- assertRedirects
@ -754,7 +758,7 @@ fournissent une collection d'assertions :
## Les tests
Personnellement habitué à pytest j'abuse de `assert`. Attention, bien
Personnellement habitué à pytest jabuse de `assert`. Attention, bien
que ça fonctionne, les messages ne sont pas aussi lisibles.
@ -766,9 +770,9 @@ On a déjà fait une `ForeignKey`, mais il existe aussi :
- ManyToManyField
::: notes
notes:
Parler des cas d'usage, et parler de `thrue`.
Parler des cas dusage, et parler de `thrue`.
# Users
@ -784,7 +788,7 @@ Chaque application peut déposer des fichiers dans son dossier `/static/`.
## Static assets
En dev ils seront tous accessibles via l'URL `/static/`.
En dev ils seront tous accessibles via lURL `/static/`.
## Static assets
@ -806,34 +810,35 @@ En parlant de prod, comment mettre un Django en prod ?
## Deployment
Django utilise le protocole `wsgi`, un standard en Python.
Django utilise les protocoles `wsgi` (synchrone) et `asgi`
(asynchrone), des classiques en Python.
## Deployment
Attention, `runserver` c'est bien en dev, mais ça n'est pas voué à partir en prod.
Attention, `runserver` cest bien en dev, mais ça nest pas fait pour la prod.
::: notes
notes:
Sérieusement.
## Deployment
`runserver` c'est un peu comme un groupe éléctrogène :
`runserver` cest un peu comme un groupe éléctrogène :
- C'est vrai que ça fonctionne.
- Cest vrai que ça fonctionne.
- Ça dépanne quand on est seul dessus, chez soi.
- Mais on alimente pas un quartier ou une ville avec.
::: notes
notes:
Trouver mieux :D
## Deployment
Nginx et Apache2 gèrent `wsgi`, d'autres serveurs aussi, probablement.
Nginx et Apache2 gèrent `wsgi`, dautres serveurs aussi, probablement.
## Deployment
@ -854,7 +859,7 @@ gunicorn -w 16 project.wsgi
## Deployment
`gunicorn` est bien derrière un `nginx` qui va s'occuper, entre autre,
`gunicorn` est bien derrière un `nginx` qui va soccuper, entre autres,
de la décapsulation HTTPS, ou de délivrer vos fichiers statiques sans
passer par Python.
@ -883,7 +888,7 @@ server {
# Bonnes pratiques
Pas de `order_by` / `fiter` / ... dans les vues, rangez ça dans les
Pas de `order_by` / `fiter` / dans les vues, rangez ça dans les
modèles (dans des *managers* personalisés).
Ça permet de nommer et de réutiliser.
@ -912,7 +917,7 @@ utilisera des applications pour le reste du code.
## Bonnes pratiques
On surcharge l'objet `User`, même si on pense ne pas en avoir besoin :
On surcharge lobjet `User`, même si on pense ne pas en avoir besoin :
```python
class User(django.contrib.auth.models.AbstractUser):
@ -925,6 +930,12 @@ et :
AUTH_USER_MODEL = ...
```
notes:
Enfin, si on est **100% sûrs** que notre application ne sera pas
réutilisée dans un contexte où une autre app redéfinit l'object
`User`
## La configuration de Django
@ -935,4 +946,6 @@ AUTH_USER_MODEL = ...
## Resources
Pour Django on avait : https://ccbv.co.uk/ (Memo: « Classy Class-Based-View »).
- La doc officielle.
- Pour Django : https://ccbv.co.uk/ (Memo : « Classy Class-Based-View »).
- Pour Django Rest Framework : https://www.cdrf.co (Memo : « Classy Django REST Framework »).