Présentation
Les bibliothèques clientes Cloud sont la méthode recommandée pour appeler les API Google Cloud à partir de vos applications. Elles utilisent les conventions et le style naturels du langage de programmation dont vous vous servez pour votre application. Les bibliothèques clientes Cloud gèrent la communication de bas niveau avec le serveur, y compris l'authentification et la logique de nouvelle tentative.
Conçu pour le scaling automatique et les hautes performances, Firestore est une base de données de documents NoSQL sans serveur, entièrement gérée et rapide qui simplifie le développement d'applications.
Cloud Storage est une solution unifiée de stockage d'objets qui permet de diffuser, d'analyser et d'archiver des données partout dans le monde.
Dans cet atelier, vous allez créer une application Python qui gère une liste de livres. Vous pouvez ajouter, modifier et supprimer des livres, et collecter des données telles que l'auteur, le titre et la description. L'application initiale stocke les données dans un dictionnaire Python en mémoire, ce qui entraîne la perte de tous les livres lorsque l'application plante.
Vous allez modifier cette application pour stocker toutes les données des livres dans Firestore, puis ajouter la possibilité de stocker une image de couverture pour un livre, que vous allez conserver dans Cloud Storage.
Objectifs de l'atelier
Dans cet atelier, vous apprendrez à effectuer les tâches suivantes :
- Créer une application Web Python Flask simple
- Créer une base de données Firestore pour stocker les données de l'application
- Créer un bucket Cloud Storage pour stocker les images à utiliser dans l'application
Préparation
Pour chaque atelier, nous vous attribuons un nouveau projet Google Cloud et un nouvel ensemble de ressources pour une durée déterminée, sans frais.
-
Cliquez sur le bouton Démarrer l'atelier. Si l'atelier est payant, un pop-up s'affiche pour vous permettre de sélectionner un mode de paiement.
Sur la gauche, vous trouverez le panneau Détails concernant l'atelier, qui contient les éléments suivants :
- Le bouton Ouvrir la console Google Cloud
- Le temps restant
- Les identifiants temporaires que vous devez utiliser pour cet atelier
- Des informations complémentaires vous permettant d'effectuer l'atelier
-
Cliquez sur Ouvrir la console Google Cloud (ou effectuez un clic droit et sélectionnez Ouvrir le lien dans la fenêtre de navigation privée si vous utilisez le navigateur Chrome).
L'atelier lance les ressources, puis ouvre la page Se connecter dans un nouvel onglet.
Conseil : Réorganisez les onglets dans des fenêtres distinctes, placées côte à côte.
Remarque : Si la boîte de dialogue Sélectionner un compte s'affiche, cliquez sur Utiliser un autre compte.
-
Si nécessaire, copiez le nom d'utilisateur ci-dessous et collez-le dans la boîte de dialogue Se connecter.
{{{user_0.username | "Username"}}}
Vous trouverez également le nom d'utilisateur dans le panneau Détails concernant l'atelier.
-
Cliquez sur Suivant.
-
Copiez le mot de passe ci-dessous et collez-le dans la boîte de dialogue Bienvenue.
{{{user_0.password | "Password"}}}
Vous trouverez également le mot de passe dans le panneau Détails concernant l'atelier.
-
Cliquez sur Suivant.
Important : Vous devez utiliser les identifiants fournis pour l'atelier. Ne saisissez pas ceux de votre compte Google Cloud.
Remarque : Si vous utilisez votre propre compte Google Cloud pour cet atelier, des frais supplémentaires peuvent vous être facturés.
-
Accédez aux pages suivantes :
- Acceptez les conditions d'utilisation.
- N'ajoutez pas d'options de récupération ni d'authentification à deux facteurs (ce compte est temporaire).
- Ne vous inscrivez pas à des essais sans frais.
Après quelques instants, la console Cloud s'ouvre dans cet onglet.
Remarque : Pour afficher un menu contenant la liste des produits et services Google Cloud, cliquez sur le menu de navigation en haut à gauche, ou saisissez le nom du service ou du produit dans le champ Recherche.
Activer Google Cloud Shell
Google Cloud Shell est une machine virtuelle qui contient de nombreux outils pour les développeurs. Elle comprend un répertoire d'accueil persistant de 5 Go et s'exécute sur Google Cloud.
Google Cloud Shell vous permet d'accéder à vos ressources Google Cloud grâce à une ligne de commande.
-
Dans la barre d'outils située en haut à droite dans la console Cloud, cliquez sur le bouton "Ouvrir Cloud Shell".

-
Cliquez sur Continuer.
Le provisionnement et la connexion à l'environnement prennent quelques instants. Une fois connecté, vous êtes en principe authentifié et le projet est défini sur votre ID_PROJET. Par exemple :

gcloud est l'outil de ligne de commande pour Google Cloud. Il est préinstallé sur Cloud Shell et permet la complétion par tabulation.
- Vous pouvez lister les noms des comptes actifs à l'aide de cette commande :
gcloud auth list
Résultat :
Credentialed accounts:
- @.com (active)
Exemple de résultat :
Credentialed accounts:
- google1623327_student@qwiklabs.net
- Vous pouvez lister les ID de projet à l'aide de cette commande :
gcloud config list project
Résultat :
[core]
project =
Exemple de résultat :
[core]
project = qwiklabs-gcp-44776a13dea667a6
Remarque : Pour consulter la documentation complète sur gcloud, accédez au guide de présentation de la gcloud CLI.
Tâche 1 : Créer et tester une application Web Python Flask simple
Dans cette tâche, vous allez créer et tester une application Python qui stocke une liste de livres.
Remarque : Dans la plupart des langages, le retrait est utilisé pour rendre le code plus lisible. En Python, le retrait sert à indiquer un bloc de code. Il doit donc être correct. Le nombre d'espaces de retrait doit être cohérent. Utiliser à la fois des espaces et des tabulations comme indicateurs de retrait peut également entraîner des problèmes. Dans cet atelier, un retrait en Python correspond à une série de quatre espaces.
Confirmer que Cloud Shell est autorisé
-
Pour vérifier si Cloud Shell est autorisé, exécutez la commande suivante dans Cloud Shell :
gcloud auth list
-
Si vous êtes invité à autoriser Cloud Shell, cliquez sur Autoriser.
Créer le répertoire de l'application
Pour créer le répertoire de l'application, exécutez la commande suivante :
mkdir ~/bookshelf
Les fichiers de l'application sont créés dans le répertoire ~/bookshelf.
Spécifier et installer les éléments requis
Un fichier d'exigences Python est un fichier texte simple qui contient la liste des dépendances nécessaires à votre projet. Pour commencer, nous avons besoin de trois modules dans notre fichier d'exigences.
Notre application est écrite avec Flask, un module de framework Web qui permet de concevoir facilement des applications Web en Python. Nous exécutons l'application à l'aide de Gunicorn, un serveur HTTP Python qui s'exécute sous Linux. Enfin, nous consignons les informations concernant notre application à l'aide de Cloud Logging.
-
Pour créer le fichier requirements, exécutez la commande suivante :
cat > ~/bookshelf/requirements.txt <<EOF
Flask==2.3.3
gunicorn==21.2.0
google-cloud-logging==3.6.0
EOF
Le fichier requirements.txt spécifie les versions de Flask, Gunicorn et Google Cloud Logging utilisées par l'application.
-
Pour installer les versions sélectionnées des dépendances, exécutez la commande suivante :
pip3 install -r ~/bookshelf/requirements.txt --user
pip est le programme d'installation de packages pour Python. La commande pip3 installe les packages spécifiés dans le fichier requirements.txt qui doivent être utilisés avec Python 3.
Créer l'implémentation de la base de données de livres
-
Pour créer le code de la base de données de livres, exécutez la commande suivante :
cat > ~/bookshelf/booksdb.py <<EOF
db = {} # global in-memory python dictionary, key should always be a string
next_id = 1 # next book ID to use
def get_next_id():
"""
Return the next ID. Automatically increments when retrieving one.
"""
global next_id
id = next_id
# next ID is 1 higher
next_id = next_id + 1
# return a string version of the ID
return str(id)
def read(book_id):
"""
Return the details for a single book.
"""
# retrieve a book from the database by ID
data = db[str(book_id)]
return data
def create(data):
"""
Create a new book and return the book details.
"""
# get a new ID for the book
book_id = get_next_id()
# set the ID in the book data
data['id'] = book_id
# store book in database
db[book_id] = data
return data
def update(data, book_id):
"""
Update an existing book, and return the updated book's details.
"""
# book ID should always be a string
book_id_str = str(book_id)
# add ID to the book data
data['id'] = book_id_str
# update book in the database
db[book_id_str] = data
return data
def delete(book_id):
"""
Delete a book in the database.
"""
# remove book from database
del db[str(book_id)]
# no return required
def list():
"""
Return a list of all books in the database.
"""
# empty list of books
books = []
# retrieve each item in database and add to the list
for k in db:
books.append(db[k])
# return the list
return books
EOF
Les livres sont stockés dans un dictionnaire Python, une structure de données permettant de stocker des paires clé/valeur. La clé doit être unique. La fonction get_next_id() crée donc un ID à chaque appel.
La fonction read(book_id) récupère l'élément correspondant à la clé book_id fournie.
La fonction create(data) ajoute le livre à la base de données en obtenant un nouvel ID, en stockant cet ID dans les données du livre, puis en stockant l'entrée de données dans le dictionnaire.
La fonction update(data, book_id) met à jour le livre dans la base de données en stockant l'ID fourni dans les données du livre, puis en stockant l'entrée de données dans le dictionnaire.
La fonction delete(book_id) supprime l'entrée de la base de données correspondant à la clé book_id fournie.
La fonction list() renvoie une liste Python contenant tous les livres de la base de données. Pour obtenir cette liste, elle passe en revue le dictionnaire et récupère chaque élément. Chaque élément stocke son ID à l'aide de la clé id.
Créer les fichiers de modèle HTML
Les modèles sont des fichiers qui contiennent à la fois des données statiques et des espaces réservés pour les données dynamiques. Vous générez un modèle pour produire un fichier HTML final.
-
Pour créer le modèle de base, exécutez la commande suivante :
mkdir ~/bookshelf/templates
cat > ~/bookshelf/templates/base.html <<EOF
<!DOCTYPE html>
<html lang="en">
<head>
<title>Bookshelf</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
</head>
<body>
<div class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<div class="navbar-brand">Bookshelf</div>
</div>
<ul class="nav navbar-nav">
<li><a href="/">Books</a></li>
</ul>
</div>
</div>
<div class="container">
{% block content %}{% endblock %}
</div>
</body>
</html>
EOF
Toutes les pages de l'application disposent de la même mise en page de base, mais leur corps est différent. Chacun des trois modèles principaux (liste, vue, formulaire) étend ce modèle de base en spécifiant le contenu à placer au centre de la page.
-
Pour créer le modèle de liste, exécutez la commande suivante :
cat > ~/bookshelf/templates/list.html <<EOF
{% extends "base.html" %}
{% block content %}
<h3>Books</h3>
<a href="/books/add" class="btn btn-success btn-sm">
<i class="glyphicon glyphicon-plus"></i>
Add book
</a>
{% for book in books %}
<div class="media">
<a href="/books/{{book.id}}">
<div class="media-body">
<h4>{{book.title}}</h4>
<p>{{book.author}}</p>
</div>
</a>
</div>
{% else %}
<p>No books found</p>
{% endfor %}
{% endblock %}
EOF
Le modèle de liste parcourt la liste des livres envoyés au modèle de base et affiche le titre, l'auteur et le lien de chaque livre. Le lien permet à l'utilisateur d'accéder à la page de vue du livre.
-
Pour créer le modèle de vue, exécutez la commande suivante :
cat > ~/bookshelf/templates/view.html <<EOF
{% extends "base.html" %}
{% block content %}
<h3>Book</h3>
<div class="btn-group">
<a href="/books/{{book.id}}/edit" class="btn btn-primary btn-sm">
<i class="glyphicon glyphicon-edit"></i>
Edit book
</a>
<a href="/books/{{book.id}}/delete" class="btn btn-danger btn-sm">
<i class="glyphicon glyphicon-trash"></i>
Delete book
</a>
</div>
<div class="media">
<div class="media-body">
<h4 class="book-title">
{{book.title}}
<small>{{book.publishedDate}}</small>
</h4>
<h5 class="book-author">By {{book.author|default('Unknown', True)}}</h5>
<p class="book-description">{{book.description}}</p>
</div>
</div>
{% endblock %}
EOF
Le modèle de vue affiche les détails du livre (titre, auteur, date de publication et description). Il comporte également deux boutons : l'un pour modifier le livre (redirige l'utilisateur vers le modèle de formulaire) et l'autre pour le supprimer (supprime le livre et redirige l'utilisateur vers la liste des livres).
-
Pour créer le modèle de formulaire, exécutez la commande suivante :
cat > ~/bookshelf/templates/form.html <<EOF
{# [START form] #}
{% extends "base.html" %}
{% block content %}
<h3>{{action}} book</h3>
<form method="POST" enctype="multipart/form-data">
<div class="form-group">
<label for="title">Title</label>
<input type="text" name="title" id="title" value="{{book.title}}" class="form-control"/>
</div>
<div class="form-group">
<label for="author">Author</label>
<input type="text" name="author" id="author" value="{{book.author}}" class="form-control"/>
</div>
<div class="form-group">
<label for="publishedDate">Date Published</label>
<input type="text" name="publishedDate" id="publishedDate" value="{{book.publishedDate}}" class="form-control"/>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea name="description" id="description" class="form-control">{{book.description}}</textarea>
</div>
<button type="submit" class="btn btn-success">Save</button>
</form>
{% endblock %}
{# [END form] #}
EOF
Le modèle de formulaire a une double finalité. Lorsque vous mettez à jour un livre, le modèle affiche les détails du livre actuel dans des zones modifiables. Lorsque vous cliquez sur le bouton Enregistrer, les champs du formulaire mis à jour sont enregistrés dans la base de données.
Lorsque vous créez un livre, les zones de détails du livre sont vides. Lorsque vous cliquez sur le bouton Enregistrer, les données du livre sont enregistrées dans la base de données en tant que nouveau livre.
Créer le fichier de code principal
-
Pour créer le fichier de code principal, exécutez la commande suivante :
cat > ~/bookshelf/main.py <<EOF
from flask import current_app, Flask, redirect, render_template
from flask import request, url_for
import logging
from google.cloud import logging as cloud_logging
import booksdb
app = Flask(__name__)
app.config.update(
SECRET_KEY='secret', # don't store SECRET_KEY in code in a production app
MAX_CONTENT_LENGTH=8 * 1024 * 1024,
)
app.debug = True
app.testing = False
# configure logging
if not app.testing:
logging.basicConfig(level=logging.INFO)
# attach a Cloud Logging handler to the root logger
client = cloud_logging.Client()
client.setup_logging()
def log_request(req):
"""
Log request
"""
current_app.logger.info('REQ: {0} {1}'.format(req.method, req.url))
@app.route('/')
def list():
"""
Display all books.
"""
log_request(request)
# get list of books
books = booksdb.list()
# render list of books
return render_template('list.html', books=books)
@app.route('/books/<book_id>')
def view(book_id):
"""
View the details of a specified book.
"""
log_request(request)
# retrieve a specific book
book = booksdb.read(book_id)
# render book details
return render_template('view.html', book=book)
@app.route('/books/add', methods=['GET', 'POST'])
def add():
"""
If GET, show the form to collect details of a new book.
If POST, create the new book based on the specified form.
"""
log_request(request)
# Save details if form was posted
if request.method == 'POST':
# get book details from form
data = request.form.to_dict(flat=True)
# add book
book = booksdb.create(data)
# render book details
return redirect(url_for('.view', book_id=book['id']))
# render form to add book
return render_template('form.html', action='Add', book={})
@app.route('/books/<book_id>/edit', methods=['GET', 'POST'])
def edit(book_id):
"""
If GET, show the form to collect updated details for a book.
If POST, update the book based on the specified form.
"""
log_request(request)
# read existing book details
book = booksdb.read(book_id)
# Save details if form was posted
if request.method == 'POST':
# get book details from form
data = request.form.to_dict(flat=True)
# update book
book = booksdb.update(data, book_id)
# render book details
return redirect(url_for('.view', book_id=book['id']))
# render form to update book
return render_template('form.html', action='Edit', book=book)
@app.route('/books/<book_id>/delete')
def delete(book_id):
"""
Delete the specified book and return to the book list.
"""
log_request(request)
# delete book
booksdb.delete(book_id)
# render list of remaining books
return redirect(url_for('.list'))
# this is only used when running locally
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8080, debug=True)
EOF
Le fichier main.py est le point d'entrée de l'application. Il implémente l'application Flask, qui spécifie le routage des URL Web, génère les modèles et gère la base de données de livres. N'hésitez pas à examiner le code, qui est bien commenté.
Tester l'application
-
Pour vérifier le contenu du répertoire "bookshelf", exécutez la commande suivante :
cd ~
ls -R bookshelf
Vous devriez voir une liste contenant deux fichiers Python, un fichier d'exigences et quatre fichiers de modèle :
bookshelf:
booksdb.py main.py requirements.txt templates
bookshelf/templates:
base.html form.html list.html view.html
-
Pour exécuter le serveur HTTP Gunicorn, exécutez la commande suivante :
cd ~/bookshelf; ~/.local/bin/gunicorn -b :8080 main:app
Si vous avez réussi à créer les fichiers, l'application doit maintenant être hébergée sur le port 8080.
-
Pour exécuter l'application dans le navigateur Web, cliquez sur Aperçu sur le Web, puis sélectionnez Prévisualiser sur le port 8080.

Un nouvel onglet s'ouvre dans le navigateur et l'application s'exécute. Il s'agit de l'URL racine, qui affiche la liste de tous les livres existants. Elle est vide pour l'instant.
Remarque : Si un message vous demande d'autoriser Cloud Shell, cliquez sur Autoriser.
-
Dans l'onglet de l'application, cliquez sur + Ajouter un livre.
-
Saisissez le titre, l'auteur, la date et la description d'un livre, réel ou fictif, puis cliquez sur Enregistrer.
Vous revenez à la page de vue, qui contient les détails du livre. Vous pouvez modifier ou supprimer le livre en cliquant sur le bouton approprié.
-
En haut de la page, cliquez sur Livres.
Vous revenez à la page de vue. Tous les livres que vous avez ajoutés s'affichent dans la liste.
Si vous le souhaitez, vous pouvez parcourir l'application pour ajouter, modifier ou supprimer des livres.
-
Dans Cloud Shell, pour quitter l'application, appuyez sur Ctrl+C.
Cliquez sur Vérifier ma progression pour valider l'objectif.
Créer et tester une application Web Python Flask simple
Tâche 2 : Utiliser Firestore pour la base de données de livres
Dans cette tâche, vous allez stocker les données des livres à l'aide d'une base de données Firestore.
L'application utilise actuellement un dictionnaire Python en mémoire. Les livres sont perdus dès que l'application se ferme ou plante. Il est préférable d'utiliser Firestore pour la base de données persistante.
Créer la base de données Firestore
-
Dans le menu de navigation (
), accédez à Afficher tous les produits > Bases de données.
-
Cliquez sur l'icône Épingler à côté de Firestore pour l'ajouter au menu de navigation, puis sur Firestore.
-
Cliquez sur Créer une base de données Firestore.
-
Dans Options de configuration, sélectionnez Firestore natif.
-
Pour Type d'emplacement, cliquez sur Région.
Dans cet atelier, vous ne devez pas utiliser le type d'emplacement Multirégional pour Firestore.
-
Pour Région, sélectionnez , puis cliquez sur Créer une base de données.
Remarque : La région exacte doit être sélectionnée pour l'emplacement. Si vous ne voyez pas cette région dans la liste déroulante, vérifiez que vous avez bien sélectionné Région (et non "Multirégional") pour Type d'emplacement.
Si aucune région n'est disponible dans la liste déroulante, annulez l'opération et revenez à la page précédente, puis réessayez d'effectuer le processus de création de la base de données.
La création de la base de données peut prendre quelques minutes. Lorsque vous ajoutez un document à une collection, elle est automatiquement créée dans Firestore. Vous n'avez donc pas besoin de créer une collection de livres pour l'instant.
La création de la base de données Firestore active l'API Firestore.
Modifier l'application afin d'exiger le client Python pour Firestore
Le client Python pour l'API Firestore permet d'accéder aux données Firestore depuis vos applications. Le nom du package de ce client est google-cloud-firestore, et la version à utiliser est 2.12.0.
- Modifiez l'application pour exiger la version 2.12.0 du package google-cloud-firestore.
Remarque : Vous pouvez utiliser l'éditeur de fichiers de votre choix, y compris nano, vi et l'éditeur Cloud Code.
-
Pour installer la dépendance mise à jour, exécutez la commande suivante :
pip3 install -r ~/bookshelf/requirements.txt --user
Modifier l'application pour stocker les données des livres dans Firestore
Maintenant que l'application exige le package google-cloud-firestore, vous pouvez l'utiliser dans l'implémentation de la base de données de livres.
Avec Firestore, les données des livres sont stockées dans la base de données Firestore. Si vos données comportent un champ unique garanti pouvant servir d'ID, vous pouvez choisir de l'utiliser comme ID dans Firestore. Dans le cas présent, les données que vous utilisez n'ont pas de champ unique. Firestore peut créer automatiquement l'ID lorsque vous créez un livre.
Voici un exemple d'utilisation du client Firestore :
from google.cloud import firestore
# get the client
db = firestore.Client()
# create a new document
data = {"name": "Sue", "role": "treasurer"}
member_ref = db.collection("members").document()
member_ref.set(data)
member_id = member_ref.get().id
# retrieve a document
member_ref = db.collection("members").document(member_id)
member = member_ref.get()
if member.exists:
print(f"Document data: {member.to_dict()}")
else:
print("Member not found.")
# update a document
new_data = {"name": "Sue", "role": "president"}
member_ref = db.Collection("members").document(member_id)
member_ref.set(new_data)
# get all documents in order
members = db.collection("members").order_by("name").stream()
for member in members:
print(f"{member.id} => {member.to_dict()}")
# delete a member
member_ref = db.Collection("members").document(member_id)
member_ref.delete()
Vous allez ensuite modifier l'implémentation de la base de données de livres pour utiliser Firestore.
Remarque : N'oubliez pas que quatre espaces sont nécessaires pour le retrait en Python.
L'implémentation actuelle utilise une variable globale nommée db, qui est un dictionnaire Python en mémoire. Elle utilise également la variable next_id et la fonction get_next_id(), qui crée les ID des éléments stockés dans le dictionnaire.
Firestore gère la création des ID pour vous. Utilisez une collection nommée books. Vous devez ajouter l'ID créé au dictionnaire Python qui contient les détails d'un livre avant de le renvoyer à l'appelant.
Remarque : Les indices masquent les modifications à apporter au code. Vous pouvez essayer d'écrire le code vous-même ou cliquer sur les boutons d'indice pour obtenir le code à ajouter.
-
Dans un éditeur de fichiers, ouvrez le fichier ~/bookshelf/booksdb.py.
-
Supprimez les lignes suivantes du fichier :
db = {} # global in-memory python dictionary, key should always be a string
next_id = 1 # next book ID to use
def get_next_id():
"""
Return the next ID. Automatically increments when retrieving one.
"""
global next_id
id = next_id
# next ID is 1 higher
next_id = next_id + 1
# return a string version of the ID
return str(id)
La base de données en mémoire et la fonctionnalité de création d'ID ne sont pas nécessaires dans cette implémentation.
-
Ajoutez du code qui importe le client Firestore.
- Créez une fonction qui convertit un document Firestore en dictionnaire.
Il ne faut pas que l'appelant ait à interpréter les documents Firestore lorsque vous lui renvoyez un livre. Les interfaces des fonctions de la base de données de livres doivent rester inchangées. Cette implémentation continue donc à renvoyer et à accepter des livres sous forme de dictionnaires Python.
Avant qu'un livre soit renvoyé, son ID doit être ajouté au dictionnaire.
Créez une fonction nommée document_to_dict() qui prend un document Firestore comme paramètre d'entrée et renvoie un dictionnaire. Le dictionnaire inclut les paires clé/valeur dans le document et renvoie également l'ID du document comme valeur de la clé id. Si le document n'existe pas, vous devez renvoyer None.
- Modifiez la fonction
read() pour récupérer un livre dans la collection books de Firestore. Votre fonction mise à jour doit appeler la fonction document_to_dict().
- Modifiez la fonction
create() pour créer un livre dans la collection books de Firestore.
- Modifiez la fonction
update() pour mettre à jour un livre dans la collection books de Firestore.
- Modifiez la fonction
delete() pour supprimer un livre dans la collection books de Firestore.
- Modifiez la fonction
list() pour renvoyer une liste de tous les livres triés par titre de la collection books de Firestore.
Et voilà ! En mettant à jour le fichier booksdb.py, vous modifiez l'application pour qu'elle utilise Firestore comme base de données sans avoir à modifier le code d'appel.
Tester l'application mise à jour
-
Dans Cloud Shell, exécutez la commande suivante :
cd ~/bookshelf; ~/.local/bin/gunicorn -b :8080 main:app
Si vous avez réussi à mettre à jour les fichiers, l'application doit maintenant être hébergée sur le port 8080.
Remarque : Si vous recevez une erreur d'importation, vérifiez que vous avez utilisé pip3 pour installer les éléments requis, qui ont été mis à jour et incluent désormais le package google.cloud.firestore.
Remarque : Si un message vous demande d'autoriser Cloud Shell, cliquez sur Autoriser.
-
Pour exécuter l'application dans le navigateur Web, cliquez sur Aperçu sur le Web, puis sélectionnez Prévisualiser sur le port 8080.
Il n'y a aucun livre dans la base de données, car les livres étaient stockés dans un dictionnaire en mémoire.
-
Dans l'onglet de l'application, cliquez sur + Ajouter un livre.
-
Saisissez les informations suivantes dans le formulaire :
| Champ |
Valeur |
| Titre |
Hamlet |
| Auteur |
William Shakespeare |
| Date de publication |
1603 |
| Description |
Un prince médite sur la vie, la mort et la vengeance, mais il fait surtout des jeux de mots. |
-
Cliquez sur Enregistrer.
Vous revenez à la page de vue, qui contient les détails du livre.
-
En haut de la page, cliquez sur Livres.
Vous êtes redirigé vers la page de la liste, dans laquelle figure Hamlet.
Remarque : Vous pouvez ajouter des livres si vous le souhaitez, mais ne modifiez pas Hamlet, car vous en aurez besoin dans la tâche suivante.
-
Dans la console Google Cloud, accédez au menu de navigation (
), puis cliquez sur Firestore.
Remarque : Même si vous êtes déjà sur la page de la console Firestore, vous devrez peut-être y accéder à nouveau pour voir la base de données.
-
Cliquez sur (par défaut).
Vous devriez voir qu'un document a été créé pour Hamlet dans la collection books.
-
Dans Cloud Shell, pour quitter l'application, appuyez sur Ctrl+C.
Résoudre les problèmes rencontrés lors de la tâche 2
Les erreurs survenues dans votre application sont actuellement affichées dans Cloud Shell. Si l'application ne fonctionne pas ou n'enregistre pas les données dans Firestore, utilisez les informations sur les erreurs pour déboguer et résoudre le problème.
Si vous avez des difficultés à faire fonctionner le code de la base de données de livres, la commande fournie dans l'indice suivant permet de remplacer l'intégralité du fichier booksdb.py par un code opérationnel.
Cliquez sur Vérifier ma progression pour valider l'objectif.
Utiliser Firestore pour la base de données de livres
Tâche 3 : Utiliser Cloud Storage pour les couvertures de livre
Dans cette tâche, vous allez utiliser Cloud Storage pour stocker des images de couverture de livre.
Une base de données n'est généralement pas l'emplacement approprié pour stocker des images. Vous ne pouvez pas stocker de fichiers dans Cloud Shell, car vous devrez héberger l'application ailleurs ultérieurement. Cloud Storage est la solution idéale pour stocker les éléments que vous souhaitez partager. Il s'agit du principal store d'objets pour Google Cloud.
Créer le bucket Cloud Storage
Pour utiliser Cloud Storage, vous devez créer un bucket Cloud Storage, qui est un conteneur de base permettant de conserver vos données.
-
Dans la console Google Cloud, accédez au menu de navigation (
) et cliquez sur Cloud Storage > Buckets.
-
Cliquez sur + Créer.
-
Pour le nom du bucket, utilisez le code suivant :
{{{ project_0.project_id | project_id }}}-covers
-
Cliquez sur Continuer.
-
Sélectionnez Région.
-
Sélectionnez .
-
Cliquez sur Continuer.
-
Laissez la classe de stockage inchangée, puis cliquez sur Continuer.
-
Décochez la case Appliquer la protection contre l'accès public sur ce bucket.
Laissez le Contrôle des accès défini sur Uniforme. Cette option permet d'utiliser des autorisations au niveau du bucket pour tous les objets ajoutés au bucket.
-
Cliquez sur Créer.
Pour que les couvertures soient visibles dans l'application, vous devez autoriser tous les utilisateurs à lire les objets du bucket.
-
Sélectionnez l'onglet Autorisations, puis cliquez sur Accorder l'accès.
-
Dans le champ Nouveaux comptes principaux, saisissez allUsers.
-
Pour le rôle, sélectionnez Anciens rôles Cloud Storage > Lecteur des anciens objets de l'espace de stockage.
Remarque : Le rôle Cloud Storage > Lecteur d'objets Storage inclut l'autorisation permettant de lister les objets d'un bucket, ce qui n'est pas nécessaire pour cette application. Le rôle Anciens rôles Cloud Storage > Lecteur des anciens objets de l'espace de stockage ne permet que de récupérer des objets, ce qui est plus approprié pour ce cas d'utilisation.
-
Cliquez sur Enregistrer.
-
Si un message vous demande de confirmer l'opération, cliquez sur Autoriser l'accès public.
Mettre à jour le fichier d'exigences
-
Dans Cloud Shell, utilisez un éditeur de fichiers pour ouvrir ~/bookshelf/requirements.txt.
-
Dans le fichier ~/bookshelf/requirements.txt, ajoutez la ligne suivante :
google-cloud-storage==2.10.0
Le fichier requirements.txt devrait maintenant se présenter comme suit :
Flask==2.3.3
gunicorn==21.2.0
google-cloud-logging==3.6.0
google-cloud-firestore==2.12.0
google-cloud-storage==2.10.0
-
Enregistrez le fichier.
-
Pour installer la dépendance mise à jour, exécutez la commande suivante dans Cloud Shell :
pip3 install -r ~/bookshelf/requirements.txt --user
Créer un code qui importe des images dans Cloud Storage
Le fichier storage.py contient le code permettant d'importer une image de couverture dans Cloud Storage.
-
Pour créer le fichier storage.py, exécutez la commande suivante :
cat > ~/bookshelf/storage.py <<EOF
from __future__ import absolute_import
import datetime
import os
from flask import current_app
from werkzeug.exceptions import BadRequest
from werkzeug.utils import secure_filename
from google.cloud import storage
def _check_extension(filename, allowed_extensions):
"""
Validates that the filename's extension is allowed.
"""
_, ext = os.path.splitext(filename)
if (ext.replace('.', '') not in allowed_extensions):
raise BadRequest(
'{0} has an invalid name or extension'.format(filename))
def _safe_filename(filename):
"""
Generates a safe filename that is unlikely to collide with existing
objects in Cloud Storage.
filename.ext is transformed into filename-YYYY-MM-DD-HHMMSS.ext
"""
filename = secure_filename(filename)
date = datetime.datetime.utcnow().strftime("%Y-%m-%d-%H%M%S")
basename, extension = filename.rsplit('.', 1)
return "{0}-{1}.{2}".format(basename, date, extension)
def upload_file(file_stream, filename, content_type):
"""
Uploads a file to a given Cloud Storage bucket and returns the public url
to the new object.
"""
_check_extension(filename, current_app.config['ALLOWED_EXTENSIONS'])
filename = _safe_filename(filename)
# build the name of the bucket
bucket_name = os.getenv('GOOGLE_CLOUD_PROJECT') + '-covers'
client = storage.Client()
# create a bucket object
bucket = client.bucket(bucket_name)
# create an object in the bucket for the specified path
blob = bucket.blob(filename)
# upload the contents of the string into the object
blob.upload_from_string(
file_stream,
content_type=content_type)
# get the public URL for the object, which is used for storing a reference
# to the image in the database and displaying the image in the app
url = blob.public_url
return url
def upload_image(img):
"""
Upload the user-uploaded file to Cloud Storage and retrieve its
publicly accessible URL.
"""
if not img:
return None
public_url = upload_file(
img.read(),
img.filename,
img.content_type
)
return public_url
EOF
La fonction upload_file() accepte un flux de fichier, un nom de fichier et le type de contenu du fichier. L'extension du nom de fichier est d'abord validée par rapport à une liste d'extensions approuvées qui sera créée lors d'une étape ultérieure. La date et l'heure actuelles sont ensuite ajoutées au nom de fichier, afin d'éviter les conflits entre des images de livre qui utilisent le même nom de fichier lors de l'importation. Le reste de la fonction interagit avec Cloud Storage.
Le nom du bucket est d'abord créé à l'aide de l'ID du projet :
bucket_name = os.getenv('GOOGLE_CLOUD_PROJECT') + '-covers'
Ensuite, une référence à un objet est créée pour le bucket et le nom de fichier spécifiés, et le contenu du fichier image est importé :
client = storage.Client()
# create a bucket object
bucket = client.bucket(bucket_name)
# create an object in the bucket for the specified path
blob = bucket.blob(filename)
Les données du fichier sont ensuite importées dans Cloud Storage, et le fichier est rendu public afin de pouvoir être affiché dans l'application Web :
# upload the contents of the string into the object
blob.upload_from_string(
file_stream,
content_type=content_type)
L'URL est ensuite renvoyée afin de pouvoir être stockée dans la base de données de livres et utilisée pour afficher l'image.
Modifier les modèles pour afficher des images de livre
Un modèle est affiché avec des données spécifiques pour produire une page Web.
Le modèle de base n'a pas besoin d'être modifié, mais les modèles de contenu (formulaire, liste, vue) doivent l'être pour afficher et importer les couvertures de livre.
-
Dans un éditeur de fichiers, ouvrez le fichier ~/bookshelf/templates/form.html.
Le formulaire doit être modifié pour collecter le fichier image.
-
En bas du formulaire, au-dessus du bouton Enregistrer, ajoutez les lignes suivantes :
<div class="form-group">
<label for="image">Cover Image</label>
<input type="file" name="image" id="image" class="form-control"/>
</div>
<div class="form-group hidden">
<label for="imageUrl">Cover Image URL</label>
<input type="text" name="imageUrl" id="imageUrl" value="{{book.imageUrl}}" class="form-control"/>
</div>
L'entrée image permet à l'utilisateur d'importer un fichier image et affiche l'image actuelle. L'entrée imageUrl est masquée, mais elle stocke l'URL publique de l'image, qui est ajoutée à l'entrée de la base de données pour le livre.
Voici à quoi doit maintenant ressembler le formulaire :
<form method="POST" enctype="multipart/form-data">
<div class="form-group">
<label for="title">Title</label>
<input type="text" name="title" id="title" value="{{book.title}}" class="form-control"/>
</div>
<div class="form-group">
<label for="author">Author</label>
<input type="text" name="author" id="author" value="{{book.author}}" class="form-control"/>
</div>
<div class="form-group">
<label for="publishedDate">Date Published</label>
<input type="text" name="publishedDate" id="publishedDate" value="{{book.publishedDate}}" class="form-control"/>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea name="description" id="description" class="form-control">{{book.description}}</textarea>
</div>
<div class="form-group">
<label for="image">Cover Image</label>
<input type="file" name="image" id="image" class="form-control"/>
</div>
<div class="form-group hidden">
<label for="imageUrl">Cover Image URL</label>
<input type="text" name="imageUrl" id="imageUrl" value="{{book.imageUrl}}" class="form-control"/>
</div>
<button type="submit" class="btn btn-success">Save</button>
</form>
-
Enregistrez le fichier.
-
Dans un éditeur de fichiers, ouvrez le fichier ~/bookshelf/templates/view.html.
L'image de livre doit s'afficher à gauche des informations sur le livre.
-
Après la ligne <div class="media">, ajoutez les lignes suivantes :
<div class="media-left">
{% if book.imageUrl %}
<img class="book-image" src="{{book.imageUrl}}" width="128" height="192" alt="book cover">
{% else %}
<img class="book-image" src="https://storage.googleapis.com/cloud-training/devapps-foundations/no-cover.png" width="128" height="192" alt="no book cover">
{% endif %}
</div>
Une nouvelle section est ajoutée à gauche des détails du livre. Si l'image de livre existe, elle apparaît dans cette section. Sinon, une image d'espace réservé s'affiche à la place.
L'élément div media se présente désormais comme suit :
<div class="media">
<div class="media-left">
{% if book.imageUrl %}
<img class="book-image" src="{{book.imageUrl}}" width="128" height="192" alt="book cover">
{% else %}
<img class="book-image" src="https://storage.googleapis.com/cloud-training/devapps-foundations/no-cover.png" width="128" height="192" alt="no book cover">
{% endif %}
</div>
<div class="media-body">
<h4 class="book-title">
{{book.title}}
<small>{{book.publishedDate}}</small>
</h4>
<h5 class="book-author">By {{book.author|default('Unknown', True)}}</h5>
<p class="book-description">{{book.description}}</p>
</div>
</div>
-
Enregistrez le fichier.
-
Dans un éditeur de fichiers, ouvrez le fichier ~/bookshelf/templates/list.html.
L'image de livre doit maintenant s'afficher à gauche de chaque livre de la liste.
-
Après la ligne <a href="/books/{{book.id}}">, ajoutez les lignes suivantes :
<div class="media-left">
{% if book.imageUrl %}
<img src="{{book.imageUrl}}" width="128" height="192" alt="book cover">
{% else %}
<img src="https://storage.googleapis.com/cloud-training/devapps-foundations/no-cover.png" width="128" height="192" alt="no book cover">
{% endif %}
</div>
Elles contiennent le même code que celui que vous avez ajouté au modèle de vue.
Modifier le fichier main.py
Le fichier de code principal doit importer l'image dans Cloud Storage lorsque le formulaire est publié, et l'URL de l'image doit être ajoutée aux données du livre.
-
Dans un éditeur de fichiers, ouvrez le fichier ~/bookshelf/main.py.
-
Une fois booksdb importé, ajoutez la ligne suivante :
import storage
-
Après les lignes d'importation, ajoutez la méthode upload_image_file() :
def upload_image_file(img):
"""
Upload the user-uploaded file to Cloud Storage and retrieve its
publicly accessible URL.
"""
if not img:
return None
public_url = storage.upload_file(
img.read(),
img.filename,
img.content_type
)
current_app.logger.info(
'Uploaded file %s as %s.', img.filename, public_url)
return public_url
Cette fonction appelle la fonction de bibliothèque que vous avez créée dans storage.py pour importer le fichier de couverture dans Cloud Storage. Elle renvoie l'URL publique du fichier importé.
-
Ajoutez la ligne suivante à la section app.config.update :
ALLOWED_EXTENSIONS=set(['png', 'jpg', 'jpeg', 'gif']),
Cela limite les extensions autorisées lors de l'importation d'une couverture de livre. Voici à quoi doit maintenant ressembler la configuration :
app.config.update(
SECRET_KEY='secret',
MAX_CONTENT_LENGTH=8 * 1024 * 1024,
ALLOWED_EXTENSIONS=set(['png', 'jpg', 'jpeg', 'gif']),
)
-
Dans la fonction add(), après la ligne data = request.form.to_dict(flat=True), ajoutez le code suivant :
image_url = upload_image_file(request.files.get('image'))
# If an image was uploaded, update the data to point to the image.
if image_url:
data['imageUrl'] = image_url
Ce code appelle la fonction upload_image_file pour importer l'image qui a été ajoutée dans le formulaire. Il ajoute également l'URL de l'image aux données du livre.
-
Dans la fonction edit(), après la ligne data = request.form.to_dict(flat=True), ajoutez le code suivant :
image_url = upload_image_file(request.files.get('image'))
# If an image was uploaded, update the data to point to the image.
if image_url:
data['imageUrl'] = image_url
Il s'agit du même code que celui que vous avez ajouté à la fonction add().
-
Enregistrez le fichier.
Tester l'application mise à jour
-
Dans Cloud Shell, exécutez la commande suivante :
cd ~/bookshelf; ~/.local/bin/gunicorn -b :8080 main:app
Si vous avez réussi à mettre à jour les fichiers, l'application doit maintenant être hébergée sur le port 8080.
Remarque : Si un message vous demande d'autoriser Cloud Shell, cliquez sur Autoriser.
-
Pour exécuter l'application dans le navigateur Web, cliquez sur Aperçu sur le Web, puis sélectionnez Prévisualiser sur le port 8080.
Les livres qui ont été ajoutés lorsque l'application utilisait Firestore doivent s'afficher. Chaque livre affiche l'image de couverture d'espace réservé, car les URL des images n'avaient pas été ajoutées à la base de données.
-
Cliquez sur Hamlet, puis sur Modifier le livre.
-
Effectuez un clic droit sur l'image de couverture du livre Hamlet, puis enregistrez-la sur votre ordinateur sous le nom hamlet.png :

-
Dans l'application Bookshelf, pour Image de couverture, cliquez sur Sélectionner un fichier.
-
Sélectionnez le fichier que vous avez téléchargé (hamlet.png), puis cliquez sur Ouvrir.
-
Cliquez sur Enregistrer.
L'image du livre Hamlet doit désormais s'afficher.
-
Dans la console Google Cloud, accédez au menu de navigation (
) et cliquez sur Cloud Storage > Buckets.
-
Cliquez sur le nom du bucket (-covers).
L'image de couverture est enregistrée dans Cloud Storage.
Cliquez sur Vérifier ma progression pour valider l'objectif.
Utiliser Cloud Storage pour les couvertures de livre
Félicitations !
Vous avez réussi à tester une application dans Cloud Shell. Vous l'avez modifiée pour qu'elle utilise les bibliothèques clientes Cloud afin de stocker ses données dans Firestore et ses images dans Cloud Storage.
Étapes suivantes et informations supplémentaires
Terminer l'atelier
Une fois l'atelier terminé, cliquez sur Terminer l'atelier. Google Cloud Skills Boost supprime les ressources que vous avez utilisées, puis efface le compte.
Si vous le souhaitez, vous pouvez noter l'atelier. Sélectionnez un nombre d'étoiles, saisissez un commentaire, puis cliquez sur Envoyer.
Le nombre d'étoiles correspond à votre degré de satisfaction :
- 1 étoile = très insatisfait(e)
- 2 étoiles = insatisfait(e)
- 3 étoiles = ni insatisfait(e), ni satisfait(e)
- 4 étoiles = satisfait(e)
- 5 étoiles = très satisfait(e)
Si vous ne souhaitez pas donner votre avis, vous pouvez fermer la boîte de dialogue.
Pour soumettre des commentaires, suggestions ou corrections, veuillez accéder à l'onglet Assistance.
Copyright 2024 Google LLC Tous droits réservés. Google et le logo Google sont des marques de Google LLC. Tous les autres noms de société et de produit peuvent être des marques des sociétés auxquelles ils sont associés.