Describe objects storage
This commit is contained in:
parent
873c8e610c
commit
58f9a56d67
|
@ -12,3 +12,70 @@ Il existe plusieurs manières de réaliser ceci ; le tout est de s'accorder sur
|
|||
Heroku (((Heroku))) - que nous verrons au prochain chapitre - propose des espaces de déploiements, mais pas d’espace de stockage.
|
||||
Il est possible d’y envoyer des fichiers utilisateurs (typiquement, des media personnalisés), mais ceux-ci seront perdus lors du redémarrage du container.
|
||||
Il est donc primordial de configurer correctement l’hébergement des fichiers média, de préférences sur un stockage compatible S3 (((S3))).
|
||||
|
||||
=== Hébergement S3
|
||||
|
||||
Pour cette partie, nous allons nous baser sur
|
||||
l’https://www.scaleway.com/en/object-storage/[Object Storage de
|
||||
Scaleway]. Ils offrent 75GB de stockage et de transfert par mois, ce qui
|
||||
va nous laisser suffisament d’espace pour jouer un peu.
|
||||
|
||||
```bash
|
||||
# requirements.txt
|
||||
|
||||
django==3.2.8
|
||||
gunicorn
|
||||
boto3
|
||||
django-storages
|
||||
```
|
||||
|
||||
image:deployment/scaleway-object-storage-bucket.png[align="center"]
|
||||
|
||||
L’idée est qu’au moment de la construction des fichiers statiques, Django aille simplement les héberger sur un espace de stockage compatible S3.
|
||||
La complexité va être de configurer correctement les différents points de terminaison. Pour héberger nos fichiers sur notre *bucket* S3, il va falloir suivre et appliquer quelques étapes dans l’ordre :
|
||||
|
||||
. Configurer un bucket compatible S3 - je parlais de Scaleway, mais il y
|
||||
en a - *littéralement* - des dizaines.
|
||||
. Ajouter la librairie `boto3`, qui s’occupera de dialoguer avec ce type de protocole
|
||||
. Ajouter la librairie `django-storage`, qui va elle s’occuper de
|
||||
faire le câblage entre le fournisseur (*via* `+boto3+`) et Django, qui
|
||||
s’attend à ce qu’on lui donne un moteur de gestion *via* la clé https://docs.djangoproject.com/en/3.2/ref/settings/#std:setting-STATICFILES_STORAGE[DJANGO_STATICFILES_STORAGE].
|
||||
|
||||
La première étape consiste à se rendre dans https://console.scaleway.com/project/credentials[la console d'administration Scaleway], pour gérer
|
||||
ses identifiants et créer un jeton.
|
||||
|
||||
image:deployment/scaleway-api-key.png[align="center"]
|
||||
|
||||
Selon la documentation de
|
||||
https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings[django-storages], de
|
||||
https://boto3.amazonaws.com/v1/documentation/api/latest/index.html[boto3]
|
||||
et de https://www.scaleway.com/en/docs/tutorials/deploy-saas-application/[Scaleway], vous aurez besoin des clés suivantes au niveau du fichier
|
||||
`settings.py` :
|
||||
|
||||
[source,python]
|
||||
----
|
||||
AWS_ACCESS_KEY_ID = os.getenv('ACCESS_KEY_ID')
|
||||
AWS_SECRET_ACCESS_KEY = os.getenv('SECRET_ACCESS_KEY')
|
||||
AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME')
|
||||
AWS_S3_REGION_NAME = os.getenv('AWS_S3_REGION_NAME')
|
||||
AWS_DEFAULT_ACL = 'public-read'
|
||||
AWS_LOCATION = 'static'
|
||||
AWS_S3_SIGNATURE_VERSION = 's3v4'
|
||||
AWS_S3_HOST = 's3.%s.scw.cloud' % (AWS_S3_REGION_NAME,)
|
||||
AWS_S3_ENDPOINT_URL = 'https://%s' % (AWS_S3_HOST, )
|
||||
|
||||
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
|
||||
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3ManifestStaticStorage'
|
||||
STATIC_URL = '%s/%s/' % (AWS_S3_ENDPOINT_URL, AWS_LOCATION)
|
||||
|
||||
# General optimization for faster delivery
|
||||
AWS_IS_GZIPPED = True
|
||||
AWS_S3_OBJECT_PARAMETERS = {
|
||||
'CacheControl': 'max-age=86400',
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Idéalement et pour respecter nos 12-facteurs, tous ces paramètres devraient pouvoir être modifiés depuis les variables d'environnement.
|
||||
====
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
\section{Kubernetes}\label{kubernetes}
|
||||
|
||||
Voir ici https://www.youtube.com/watch?v=NAOsLaB6Lfc ( La vidéo dure
|
||||
5h\ldots)
|
|
@ -125,8 +125,6 @@ régulier :
|
|||
Europe/Brussels... done
|
||||
....
|
||||
|
||||
(+ voir comment récupérer les backups)
|
||||
|
||||
....
|
||||
# Copié/collé de https://cookiecutter- django.readthedocs.io/en/latest/deployment-on-heroku.html
|
||||
heroku create --buildpack https://github.com/heroku/heroku-buildpack-python
|
||||
|
@ -180,93 +178,20 @@ expliquera la commande pour le protocole WSGI.
|
|||
|
||||
=== Hébergement S3
|
||||
|
||||
....
|
||||
# requirements.txt
|
||||
django==3.2.8
|
||||
gunicorn
|
||||
boto3
|
||||
django-storages
|
||||
....
|
||||
Au chapitre précédent, nous avons défini un ensemble de plusieurs variables pouvant être configurées.
|
||||
Leur attribuer une valeur peut être réalisé depuis la console d'administration d'Heroku :
|
||||
|
||||
Pour cette partie, nous allons nous baser sur
|
||||
l’https://www.scaleway.com/en/object-storage/[Object Storage de
|
||||
Scaleway]. Ils offrent 75GB de stockage et de transfert par mois, ce qui
|
||||
va nous laisser suffisament d’espace pour jouer un peu.
|
||||
|
||||
image:images/deployment/scaleway-object-storage-bucket.png[image]
|
||||
|
||||
L’idée est qu’au moment de la construction des fichiers statiques,
|
||||
Django aille simplement les héberger sur un espace de stockage
|
||||
compatible S3. La complexité va être de configurer correctement les
|
||||
différents points de terminaison. Pour héberger nos fichiers sur notre
|
||||
*bucket* S3, il va falloir suivre et appliquer quelques étapes dans
|
||||
l’ordre :
|
||||
|
||||
. Configurer un bucket compatible S3 - je parlais de Scaleway, mais il y
|
||||
en a - *littéralement* - des dizaines.
|
||||
. Ajouter la librairie `+boto3+`, qui s’occupera de "parler" avec ce
|
||||
type de protocole
|
||||
. Ajouter la librairie `+django-storage+`, qui va elle s’occuper de
|
||||
faire le câblage entre le fournisseur (*via* `+boto3+`) et Django, qui
|
||||
s’attend à ce qu’on lui donne un moteur de gestion *via* la clé
|
||||
[`+DJANGO_STATICFILES_STORAGE+`](https://docs.djangoproject.com/en/3.2/ref/settings/#std:setting-STATICFILES_STORAGE).
|
||||
|
||||
La première étape consiste à se rendre dans [la console
|
||||
Scaleway](https://console.scaleway.com/project/credentials), pour gérer
|
||||
ses identifiants et créer un jeton.
|
||||
|
||||
image:images/deployment/scaleway-api-key.png[image]
|
||||
|
||||
Selon la documentation de
|
||||
https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings[django-storages],
|
||||
de
|
||||
https://boto3.amazonaws.com/v1/documentation/api/latest/index.html[boto3]
|
||||
et de
|
||||
https://www.scaleway.com/en/docs/tutorials/deploy-saas-application/[Scaleway],
|
||||
vous aurez besoin des clés suivantes au niveau du fichier
|
||||
`+settings.py+` :
|
||||
|
||||
[source,python]
|
||||
----
|
||||
AWS_ACCESS_KEY_ID = os.getenv('ACCESS_KEY_ID')
|
||||
AWS_SECRET_ACCESS_KEY = os.getenv('SECRET_ACCESS_KEY')
|
||||
AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME')
|
||||
AWS_S3_REGION_NAME = os.getenv('AWS_S3_REGION_NAME')
|
||||
AWS_DEFAULT_ACL = 'public-read'
|
||||
AWS_LOCATION = 'static'
|
||||
AWS_S3_SIGNATURE_VERSION = 's3v4'
|
||||
AWS_S3_HOST = 's3.%s.scw.cloud' % (AWS_S3_REGION_NAME,)
|
||||
AWS_S3_ENDPOINT_URL = 'https://%s' % (AWS_S3_HOST, )
|
||||
|
||||
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
|
||||
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3ManifestStaticStorage'
|
||||
STATIC_URL = '%s/%s/' % (AWS_S3_ENDPOINT_URL, AWS_LOCATION)
|
||||
|
||||
# General optimization for faster delivery
|
||||
AWS_IS_GZIPPED = True
|
||||
AWS_S3_OBJECT_PARAMETERS = {
|
||||
'CacheControl': 'max-age=86400',
|
||||
}
|
||||
----
|
||||
|
||||
Cette partie pourrait éventuellement se trouver dans un fichier
|
||||
`+heroku.py+`, que vous pourriez placer à côté de votre configuration
|
||||
applicative. Le paramètre `+–settings=+` pourrait alors pointer vers ce
|
||||
nouveau fichier, pour ne pas polluer l’environnement par défaut.
|
||||
|
||||
Configurez-les dans la console d’administration d’Heroku:
|
||||
|
||||
image:images/deployment/heroku-vars-reveal.png[image]
|
||||
image:deployment/heroku/vars-reveal.png[image]
|
||||
|
||||
Lors de la publication, vous devriez à présent avoir la sortie suivante,
|
||||
qui sera confirmée par le *bucket*:
|
||||
|
||||
....
|
||||
remote: -----> $ python manage.py collectstatic --noinput
|
||||
remote: 128 static files copied, 156 post-processed
|
||||
....
|
||||
```bash
|
||||
remote: -----> $ python manage.py collectstatic --noinput
|
||||
remote: 128 static files copied, 156 post-processed
|
||||
```
|
||||
|
||||
image:images/deployment/gwift-cloud-s3.png[image]
|
||||
image:deployment/gwift-cloud-s3.png[image]
|
||||
|
||||
Sources complémentaires:
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ grand.
|
|||
|
||||
Conceptuellement, c’est pourtant la manière de faire qui permettra d’avoir quelque chose à présenter très rapidement: à partir du moment où vous aurez un modèle de données, vous aurez accès, grâce à cet ORM à:
|
||||
|
||||
. Des migrations de données et la possibilité de faire évoluer votre modèle,
|
||||
. Des migrations de données et la possibilité de faire évoluer votre modèle en corrélation avec votre couche de persistance,
|
||||
. Une abstraction entre votre modélisation et la manière dont les données sont représentées _via_ un moteur de base de données relationnelles,
|
||||
. Une interface d’administration auto-générée,
|
||||
. Un mécanisme de formulaires HTML complet, pratique à utiliser, orienté objet et logique à faire évoluer,
|
||||
|
@ -63,6 +63,8 @@ Si nous souhaitons ajouter une fonctionnalité permettant de calculer l’aire p
|
|||
. Soit ajouter une classe de _visite_ qui ajoute cette fonction de calcul d’aire
|
||||
. Soit modifier notre modèle pour que chaque structure hérite d’une classe de type `Shape`, qui implémentera elle-même ce calcul d’aire.
|
||||
|
||||
==== Visitor
|
||||
|
||||
Dans le premier cas, nous pouvons procéder de la manière suivante:
|
||||
|
||||
[source,python]
|
||||
|
@ -84,6 +86,8 @@ class Geometry:
|
|||
|
||||
----
|
||||
|
||||
==== Notions d'héritage
|
||||
|
||||
Dans le second cas, l’implémentation pourrait évoluer de la manière suivante:
|
||||
|
||||
[source,python]
|
||||
|
|
|
@ -72,57 +72,47 @@ Les querysets vont cependant plus loin, et permettent de réaliser des :
|
|||
|
||||
==== Opérations logiques
|
||||
|
||||
Les opérat
|
||||
|
||||
....
|
||||
Pour un `AND`, il suffit de chaîner les conditions. ** trouver un exemple ici ** :-)
|
||||
....
|
||||
|
||||
....
|
||||
Mais en gros : bidule.objects.filter(condition1, condition2)
|
||||
....
|
||||
|
||||
....
|
||||
Il existe deux autres options : combiner deux querysets avec l'opérateur `&` ou combiner des Q objects avec ce même opérateur.
|
||||
....
|
||||
|
||||
Soit encore combiner des filtres:
|
||||
Les opérations logiques "de base" sont accessibles très facilement et peuvent être chaînées de manière totalement séquentielle :
|
||||
|
||||
[source,python]
|
||||
----
|
||||
from core.models import Wish
|
||||
|
||||
Wish.objects
|
||||
Wish.objects.filter(name__icontains="test").filter(name__icontains="too")
|
||||
Book.filter(title__contains="a").filter(title__contains="b") <1>
|
||||
Book.filter(title__contains="a", title__contains="b") <2>
|
||||
----
|
||||
<1> Ceci nous donnera d'abord les livres qui contiennent le caractère "a" dans leur titre, puis appliquera un deuxième filtre sur ceux qui contiennent *également* le caractère "b".
|
||||
<2> Cette deuxième représentation est totalement identique à la première.
|
||||
|
||||
* Ca, c’est notre manager.
|
||||
* Et là, on chaîne les requêtes pour composer une recherche sur tous les
|
||||
souhaits dont le nom contient (avec une casse insensible) la chaîne
|
||||
"test" et dont le nom contient la chaîne "too".
|
||||
|
||||
Pour un ’OR’, on a deux options :
|
||||
|
||||
. Soit passer par deux querysets, typiuqment `queryset1 queryset2`
|
||||
. Soit passer par des `Q objects`, que l’on trouve dans le namespace
|
||||
`django.db.models`.
|
||||
Une troisième option consiste à utiliser les `Q objects`, que nous pouvons trouver dans l'espace de noms `django.db`.
|
||||
Ces éléments peuvent servir autant à appliquer un filtre `AND` que `OR` :
|
||||
|
||||
[source,python]
|
||||
----
|
||||
from django.db.models import Q
|
||||
from django.db.models import Q
|
||||
|
||||
condition1 = Q(...)
|
||||
condition2 = Q(...)
|
||||
|
||||
bidule.objects.filter(condition1 | condition2)
|
||||
Book.objects.filter(
|
||||
Q(title__contains="a"),
|
||||
Q(title__contains="b")
|
||||
) <1>
|
||||
|
||||
Book.objects.filter(
|
||||
Q(title__contains="a") | Q(title__contains="b")
|
||||
) <2>
|
||||
----
|
||||
<1> Nous cherchons les livres dont le titre contient "a" *ET* "b",
|
||||
<2> Nous cherchons ici les livres dont le titre contient "a" *OU* "b"
|
||||
|
||||
L’opérateur inverse (_NOT_)
|
||||
De la même manière, la négation peut être représentée _via_ un `Q object` sur lequel nous appliquerons l'opérateur `~` :
|
||||
|
||||
Idem que ci-dessus : soit on utilise la méthode `exclude` sur le
|
||||
queryset, soit l’opérateur `~` sur un Q object;
|
||||
[source,python]
|
||||
----
|
||||
from django.db.models import Q
|
||||
|
||||
|
||||
Book.objects.filter(~Q(title__contains="a")) <1>
|
||||
----
|
||||
<1> Nous cherchons ici les livres dont le titre *ne contient pas* le caractère "a".
|
||||
|
||||
[TIP]
|
||||
====
|
||||
Si on veut connaître la requête SQL sous-jacente à l’exécution du queryset, il suffit d’appeler la fonction `str()` sur la propriété `query`:
|
||||
|
|
|
@ -119,3 +119,5 @@ include::book/deployment/files-storage.adoc[]
|
|||
include::book/deployment/paas.adoc[]
|
||||
|
||||
include::book/deployment/cloud-native.adoc[]
|
||||
|
||||
include::book/deployment/kubernetes.adoc[]
|
||||
|
|
|
@ -10,6 +10,19 @@ En restant dans les sentiers battus, votre projet suivra un patron de conception
|
|||
Dans un pattern MVC classique, la traduction immédiate du contrôleur est une vue.
|
||||
Et comme nous le verrons par la suite, la vue est en fait le template.
|
||||
La principale différence avec un modèle MVC concerne le fait que la vue ne s’occupe pas du routage des URLs ; ce point est réalisé par un autre composant, interne au framework, graĉe aux différentes routes définies dans les fichiers `urls.py`.
|
||||
A part ce point, le pattern MVC cite:[design_patterns(520)] est respecté sous réserve de suivre ces correspondances :
|
||||
|
||||
[cols="1,1"]
|
||||
|===
|
||||
|Model
|
||||
|`models.py`
|
||||
|
||||
|View
|
||||
|Template
|
||||
|
||||
|Controler
|
||||
|`views.py`
|
||||
|===
|
||||
|
||||
* Le *modèle* (que l'on retrouvera souvent dans le module `models.py`) fait le lien avec la base de données et permet de définir les champs et leur type à associer à une table. Grosso modo, une table SQL correspondra à une classe d’un modèle Django.
|
||||
* La *vue* (`views.py`), qui joue le rôle de contrôleur : a priori, tous les traitements, la récupération des données, etc. doit passer par ce composant et ne doit (pratiquement) pas être généré à la volée, directement à l’affichage d’une page. En d’autres mots, la vue sert de pont entre les données gérées par la base et l’interface utilisateur.
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 75 KiB |
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
Binary file not shown.
After Width: | Height: | Size: 109 KiB |
|
@ -99,6 +99,14 @@
|
|||
year = {2015},
|
||||
publisher = {Packt Publishing}
|
||||
}
|
||||
@book{design_patterns,
|
||||
title = {Head First Design Patterns},
|
||||
booktitle = {Building Extensible & Maintainable Object-Oriented Software},
|
||||
edition = {2nd edition},
|
||||
year = {2020},
|
||||
publisher = {O'Reilly Media, Inc.},
|
||||
isbn = {9781492078005}
|
||||
}
|
||||
@book{unix_philosophy,
|
||||
author = {Eric S. Raymond},
|
||||
year = {2004},
|
||||
|
|
Loading…
Reference in New Issue