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 https://mdk.fr
::: notes notes:
Introduce yourself! Introduce yourself!
# Django # 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. > The web framework for perfectionists with deadlines.
## Django : qui l'utilise ? ## Django : qui lutilise ?
- Instagram, Pineterest, Mozilla, Disqus, BitBucket, … - Instagram
- Pineterest
![](static/stats.png) - Mozilla
- National Geographic
- [Washington Post](https://www.djangoproject.com/weblog/2005/dec/08/congvotes/)
- ...
## Vocabulaire ## Vocabulaire
@ -55,18 +58,18 @@ Une fois dans le projet, pour créer une application, une commande :
python manage.py startapp watch python manage.py startapp watch
``` ```
::: notes notes:
(et ajout dans `settings.py`) (et ajout dans `settings.py`)
## La théorie — Modèle ## 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. Ça rappelle un ORM, mais ça permet beaucoup plus de choses en Django.
::: notes notes:
- admin - admin
- forms - forms
@ -85,14 +88,14 @@ class Website(models.Model):
last_check = models.DateTimeField(auto_now_add=True) 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 les differents endroits ou Django peut reutiliser cette information
(widgets, validation, ...). (widgets, validation, ...).
## Première interface d'admin ## Première interface dadmin
En une ligne, pourquoi pas : En une ligne, pourquoi pas :
@ -107,14 +110,14 @@ admin.site.register(Website)
- sqlite - 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 configurer un serveur de base de donnée :D
Leur expliquer que sqlite est utilisé dans les applications : pas Leur expliquer que sqlite est utilisé dans les applications : pas
besoin d'installer un postgresql pour utiliser Firefox, pourtant besoin dinstaller un postgresql pour utiliser Firefox, pourtant
Firefox a besoin d'une base de donnée. Firefox a besoin dune base de donnée.
## La théorie — La DB ## La théorie — La DB
@ -124,15 +127,15 @@ python manage.py makemigrations
python manage.py migrate python manage.py migrate
``` ```
::: notes notes:
On expliquera plus tard, leur dire que ça crée la DB et que le but 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 ```bash
python manage.py createsuperuser python manage.py createsuperuser
@ -154,15 +157,15 @@ cd project
python manage.py startapp watch python manage.py startapp watch
``` ```
::: notes notes:
Biiien prendre le temps d'expliquer l'arborescence, de se promener, Biiien prendre le temps dexpliquer larborescence, de se promener,
d'y lire les commentaires. dy lire les commentaires.
## La pratique ## La pratique
Ajout de l'app `watch` dans `project/settings.py` : Ajout de lapp `watch` dans `project/settings.py` :
```python ```python
INSTALLED_APPS = [ 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`. Et ça dans `watch/admin.py`.
@ -204,19 +207,19 @@ python manage.py makemigrations
python manage.py migrate python manage.py migrate
``` ```
::: notes notes:
Expliquer les deux étapes. Expliquer les deux étapes.
## Les modèles ## 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 ```bash
python manage.py createsuperuser python manage.py createsuperuser
@ -224,31 +227,31 @@ python manage.py createsuperuser
## Terminé ## Terminé
On a terminé, on peut essayer maintenant ? On a terminé, on peut essayer maintenant ?
```bash ```bash
python manage.py runserver python manage.py runserver
``` ```
::: notes notes:
Leur faire faire ça dans un **autre** shell. 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 Les modèles, leurs `fields` ne servent donc pas qua lORM, cette
interface d'admin nous à demandé une ligne de code. 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 ## 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. 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 Créez le modèle `Check` avec les champs `is_up`, `date`, `website`, et
`message`. `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. Les faire tester ça.
## Personalisons ## Personalisons
Dans chaque modèle, un `__str__` aide l'admin à être lisible. Dans chaque modèle, un `__str__` aide ladmin à être lisible.
## Personalisons ## 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 ```python
@admin.register(Website) @admin.register(Website)
@ -296,12 +299,12 @@ class CheckAdmin(admin.ModelAdmin):
# Les URLs & les vues # 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 ## 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 ```python
from watch import views 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 ## Les URLs
@ -341,18 +344,18 @@ from django.urls import include
path("", include("watch.urls")), path("", include("watch.urls")),
``` ```
::: notes notes:
C'est pratique pour « ancrer » un ensemble de chemin sous un autre Cest pratique pour « ancrer » un ensemble de chemin sous un autre
chemin : pour se faire une hierarchie. chemin : pour se faire une hierarchie.
## namespaces ## namespaces
Les espaces de nommage permettent de désambiguer les urls nommées : 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 `index` est le nom de la page daccueil de linterface dadmin ou de la
page d'accueil de votre application... page daccueil de votre application ?
## namespaces ## namespaces
@ -364,9 +367,9 @@ Avec les espaces de nommage, on a donc :
sans ambiguité. sans ambiguité.
::: notes notes:
Utiliez-en, c'est bien. Utiliez-en, cest bien.
## Les vues ## 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 ## Les vues
@ -425,7 +428,7 @@ Donc dans `watch/templates/watch/index.html` :
</html> </html>
``` ```
::: notes notes:
La création du dossier `templates/` est typiquement quelque chose que La création du dossier `templates/` est typiquement quelque chose que
`runserver` ne voit pas, il faut le redémarrer. `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 ## 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 ## Les vues
@ -447,18 +450,18 @@ def index(request):
{"websites": Website.objects.all()}) {"websites": Website.objects.all()})
``` ```
::: notes notes:
Ne pas oublier les imports… Ne pas oublier les imports…
Premier apperçu de l'ORM en passant. Premier apperçu de lORM en passant.
## Les vues ## Les vues
Digression : 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. des modèles.
@ -485,7 +488,7 @@ class WebsiteListView(ListView):
## Les templates ## 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… page…
@ -505,14 +508,14 @@ En utilisant `extends`, on peut réutiliser des templates.
python -m pip install django-debug-toolbar 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 ```pycon
>>> from watch.models import Website >>> from watch.models import Website
@ -524,8 +527,8 @@ Essayer `.all`, `.filter`, `.get`, `.order_by`, et les slices.
## Les *Managers* ## Les *Managers*
Les *managers* représentent une table, il sont accessible via Les *managers* représentent une table, ils sont accessibles via
l'attribut `objects` d'un modèle. lattribut `objects` dun modèle.
Ses opérations (des méthodes) renvoient des `queryset`s. 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* ## Les *Queryset*
Représentent un ensemble de lignes de la base de donnée. Ils ont les 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 - filter
- get - get
@ -561,8 +564,8 @@ Out[3]: <QuerySet [<Website: mdk.fr>]>
## Les *Queryset* ## Les *Queryset*
Pour ceux qui ont fait du SQL c'est un "lazy select" : c'est un Pour ceux qui ont fait du SQL cest un "lazy select" : cest un
`SELECT` qui ne s'éxécutera que si nécessaire. `SELECT` qui ne séxécutera que si nécessaire.
## Les *RelatedManager* ## Les *RelatedManager*
@ -577,7 +580,7 @@ Out[4]: <QuerySet [<Check: mdk.fr is up>, <Check: mdk.fr is up>]>
# Forms # 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. 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 ```python
class WebsiteForm(forms.Form): class WebsiteForm(forms.Form):
host = forms.CharField(label='Website hostname', host = forms.CharField(label="Website hostname",
max_length=512) max_length=512)
``` ```
@ -610,7 +613,7 @@ On le donne au template :
## Forms ## Forms
On l'affiche dans le template : On laffiche dans le template :
```html ```html
<form action="/" method="post"> <form action="/" method="post">
@ -639,7 +642,7 @@ class WebsiteForm(forms.ModelForm):
## Widgets ## 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 possible de le changer, par exemple si vous préférez un `<input` à un
`<textarea` ou vice-versa. `<textarea` ou vice-versa.
@ -655,7 +658,7 @@ if request.method == "POST":
Website.objects.create(host=form.cleaned_data["host"]) Website.objects.create(host=form.cleaned_data["host"])
``` ```
::: notes notes:
Django peut donc vérifier que les champs non-empty sont bien pas Django peut donc vérifier que les champs non-empty sont bien pas
vides, que les longuers sont respectées, etc… vides, que les longuers sont respectées, etc…
@ -663,12 +666,12 @@ vides, que les longuers sont respectées, etc…
# Les tests # Les tests
Tester c'est douter. Tester cest douter.
::: notes notes:
Ou pas. Avoir les tests qui passent avant de pousser, avant de merger 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 ## Les fixtures
@ -680,21 +683,22 @@ On appelera ça des « fixtures ».
## Les fixtures ## Les fixtures
Le moyen simple de créer des fixtures est d'utiliser les données que Le moyen simple de créer des fixtures est dutiliser les données que
vous avez crées via l'admin : vous avez crée via ladmin :
```bash ```bash
./manage.py dumpdata -o watch/fixtures/initial.json ./manage.py dumpdata -o watch/fixtures/initial.json
``` ```
::: notes notes:
Créez le dossier d'abord ;) Créez le dossier dabord ;)
## Les fixtures ## 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 ```bash
git clone … 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 placera les tests de nos applications dans le dossier `tests` ou le
fichier `tests.py` des applications. fichier `tests.py` des applications.
::: notes notes:
Habituellement on dit que le nom du fichier n'a aucune importance, que Habituellement on dit que le nom du fichier na aucune importance, que
seul le succès de l'import compte. Ici c'est le contraire, le nom du seul le succès de limport compte. Ici cest le contraire, le nom du
fichier doit commencer par `test` pour être trouvé. fichier doit commencer par `test` pour être trouvé.
## Les tests ## Les tests
Les tests s'exécutent via : Les tests sexécutent via :
```bash ```bash
python manage.py test python manage.py test
@ -743,7 +747,7 @@ class WatchTestCase(TestCase):
## Les tests ## Les tests
Pour vérifier que tout se passe bien `unittest` et `Django` nous Pour vérifier que tout se passe bien `unittest` et `Django` nous
fournissent une collection d'assertions : fournissent une collection dassertions :
- assertTemplateUsed - assertTemplateUsed
- assertRedirects - assertRedirects
@ -754,7 +758,7 @@ fournissent une collection d'assertions :
## Les tests ## 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. 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 - ManyToManyField
::: notes notes:
Parler des cas d'usage, et parler de `thrue`. Parler des cas dusage, et parler de `thrue`.
# Users # Users
@ -784,7 +788,7 @@ Chaque application peut déposer des fichiers dans son dossier `/static/`.
## Static assets ## Static assets
En dev ils seront tous accessibles via l'URL `/static/`. En dev ils seront tous accessibles via lURL `/static/`.
## Static assets ## Static assets
@ -806,34 +810,35 @@ En parlant de prod, comment mettre un Django en prod ?
## Deployment ## Deployment
Django utilise le protocole `wsgi`, un standard en Python. Django utilise les protocoles `wsgi` (synchrone) et `asgi`
(asynchrone), des classiques en Python.
## Deployment ## 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. Sérieusement.
## Deployment ## 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. - Ça dépanne quand on est seul dessus, chez soi.
- Mais on alimente pas un quartier ou une ville avec. - Mais on alimente pas un quartier ou une ville avec.
::: notes notes:
Trouver mieux :D Trouver mieux :D
## Deployment ## Deployment
Nginx et Apache2 gèrent `wsgi`, d'autres serveurs aussi, probablement. Nginx et Apache2 gèrent `wsgi`, dautres serveurs aussi, probablement.
## Deployment ## Deployment
@ -854,7 +859,7 @@ gunicorn -w 16 project.wsgi
## Deployment ## 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 de la décapsulation HTTPS, ou de délivrer vos fichiers statiques sans
passer par Python. passer par Python.
@ -883,7 +888,7 @@ server {
# Bonnes pratiques # 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). modèles (dans des *managers* personalisés).
Ça permet de nommer et de réutiliser. Ça permet de nommer et de réutiliser.
@ -912,7 +917,7 @@ utilisera des applications pour le reste du code.
## Bonnes pratiques ## 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 ```python
class User(django.contrib.auth.models.AbstractUser): class User(django.contrib.auth.models.AbstractUser):
@ -925,6 +930,12 @@ et :
AUTH_USER_MODEL = ... 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 ## La configuration de Django
@ -935,4 +946,6 @@ AUTH_USER_MODEL = ...
## Resources ## 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 »).