Django : Proofreading.
This commit is contained in:
parent
7341368eba
commit
5e69beabe4
|
@ -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 d’applications web populaire et robuste.
|
||||
|
||||
> The web framework for perfectionists with deadlines.
|
||||
|
||||
|
||||
## Django : qui l'utilise ?
|
||||
## Django : qui l’utilise ?
|
||||
|
||||
- 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 d’une 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 d’expliquer 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 d’admin
|
||||
|
||||
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 qu’on va devoir s’installer 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 d’installer un postgresql pour utiliser Firefox, pourtant
|
||||
Firefox a besoin d’une 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 c’est surtout d’aller tester ça :)
|
||||
|
||||
|
||||
## L'interface d'administration
|
||||
## L’interface d’administration
|
||||
|
||||
On a une DB, mais pas encore d'utilisateur admin dedans :
|
||||
On a une DB, mais pas encore d’utilisateur 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 d’expliquer l’arborescence, de se promener,
|
||||
d’y lire les commentaires.
|
||||
|
||||
|
||||
## La pratique
|
||||
|
||||
Ajout de l'app `watch` dans `project/settings.py` :
|
||||
Ajout de l’app `watch` dans `project/settings.py` :
|
||||
|
||||
```python
|
||||
INSTALLED_APPS = [
|
||||
|
@ -185,7 +188,7 @@ class Website(models.Model):
|
|||
```
|
||||
|
||||
|
||||
## Première interface d'admin
|
||||
## Première interface d’admin
|
||||
|
||||
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` d’abord.
|
||||
|
||||
|
||||
## L'interface d'administration
|
||||
## L’interface d’administration
|
||||
|
||||
On a une DB, mais pas encore d'utilisateur admin dedans :
|
||||
On a une DB, mais pas encore d’utilisateur 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 l’interface d’admin, créer quelques sites...
|
||||
|
||||
|
||||
## L'interface d'administration
|
||||
## L’interface d’administration
|
||||
|
||||
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 qu’a l’ORM, cette
|
||||
interface d’admin nous a demandé une ligne de code.
|
||||
|
||||
::: notes
|
||||
notes:
|
||||
|
||||
Si ce n'est pas déjà fait, leur faire ajouter des `__str__`.
|
||||
Si ce n’est 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 l’interface d’admin,
|
||||
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 d’un `models.ForeignKey`, RTFM.
|
||||
|
||||
|
||||
## L'admin
|
||||
## L’admin
|
||||
|
||||
Ajoutez une interface d'admin pour ce modèle, et ajoutez à la main quelques « *checks* ».
|
||||
Ajoutez une interface d’admin 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 l’admin à ê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 qu’on 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 d’accueil :
|
||||
|
||||
```python
|
||||
from watch import views
|
||||
|
@ -313,9 +316,9 @@ urlpatterns = [
|
|||
]
|
||||
```
|
||||
|
||||
::: notes
|
||||
notes:
|
||||
|
||||
C'est un `path`, un chemin, c'est le chemin vide.
|
||||
C’est un `path`, un chemin, c’est 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.
|
||||
C’est 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 d’accueil de l’interface d’admin ou de la
|
||||
page d’accueil 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, c’est 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.
|
||||
C’est bien mais écrire du HTML dans du Python c’est 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 l’ORM 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 s’appuyer 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 l’entê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`.
|
||||
L’ajouter dans `settings.py` et `urls.py`.
|
||||
|
||||
|
||||
# L'ORM
|
||||
# L’ORM
|
||||
|
||||
## L'ORM
|
||||
## L’ORM
|
||||
|
||||
C'est l'occasion de sortir un `python manage.py shell`.
|
||||
C’est l’occasion 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
|
||||
l’attribut `objects` d’un 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 c’est un "lazy select" : c’est 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 l’admin, 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 l’affiche 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 d’un 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 c’est 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 c’est 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 d’utiliser les données que
|
||||
vous avez crée via l’admin :
|
||||
|
||||
```bash
|
||||
./manage.py dumpdata -o watch/fixtures/initial.json
|
||||
```
|
||||
|
||||
::: notes
|
||||
notes:
|
||||
|
||||
Créez le dossier d'abord ;)
|
||||
Créez le dossier d’abord ;)
|
||||
|
||||
|
||||
## 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 qu’ils 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 n’a aucune importance, que
|
||||
seul le succès de l’import compte. Ici c’est le contraire, le nom du
|
||||
fichier doit commencer par `test` pour être trouvé.
|
||||
|
||||
|
||||
## Les tests
|
||||
|
||||
Les tests s'exécutent via :
|
||||
Les tests s’exé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 d’assertions :
|
||||
|
||||
- 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 j’abuse 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 d’usage, 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 l’URL `/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` c’est bien en dev, mais ça n’est pas fait pour la prod.
|
||||
|
||||
::: notes
|
||||
notes:
|
||||
|
||||
Sérieusement.
|
||||
|
||||
|
||||
## Deployment
|
||||
|
||||
`runserver` c'est un peu comme un groupe éléctrogène :
|
||||
`runserver` c’est un peu comme un groupe éléctrogène :
|
||||
|
||||
- C'est vrai que ça fonctionne.
|
||||
- C’est 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`, d’autres 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 s’occuper, 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 l’objet `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 »).
|
||||
|
|
Loading…
Reference in New Issue