Descripción general
Las bibliotecas cliente de Cloud son el método recomendado para llamar a las APIs de Google Cloud desde tus aplicaciones. Las bibliotecas cliente de Cloud usan las convenciones y el estilo naturales del lenguaje de programación que utilizas en tu aplicación. Las bibliotecas cliente de Cloud controlan la comunicación de bajo nivel con el servidor, incluida la autenticación y la lógica de reintentos.
Firestore es una base de datos NoSQL de documentos rápida, completamente administrada y sin servidores diseñada para el escalado automático, el alto rendimiento y la facilidad en el desarrollo de aplicaciones.
Cloud Storage es un almacenamiento de objetos unificado que te permite entregar, analizar y archivar datos en cualquier parte del mundo.
En este lab, crearás una aplicación de Python que administre una lista de libros. Puedes agregar, editar y borrar libros, y recopilar datos como autor, título y descripción. La aplicación inicial almacena los datos en un diccionario de Python en memoria, lo que hace que se pierdan todos los libros cuando falla la aplicación.
Modificarás esta aplicación para almacenar todos los datos del libro en Firestore y, luego, agregarás la capacidad de almacenar una imagen de portada para un libro, que persistirá en Cloud Storage.
Qué aprenderás
En este lab, aprenderás a realizar las siguientes tareas:
- Crear una aplicación web simple con Flask para Python
- Crear una base de datos de Firestore para almacenar los datos de aplicación
- Crear un bucket de Cloud Storage para almacenar las imágenes que se usarán en la aplicación
Configuración y requisitos
En cada lab, recibirás un proyecto de Google Cloud y un conjunto de recursos nuevos por tiempo limitado y sin costo adicional.
-
Haz clic en el botón Comenzar lab. Si debes pagar por el lab, se abrirá una ventana emergente para que selecciones tu forma de pago.
A la izquierda, se encuentra el panel Detalles del lab, que tiene estos elementos:
- El botón Abrir la consola de Google Cloud
- El tiempo restante
- Las credenciales temporales que debes usar para el lab
- Otra información para completar el lab, si es necesaria
-
Haz clic en Abrir la consola de Google Cloud (o haz clic con el botón derecho y selecciona Abrir el vínculo en una ventana de incógnito si ejecutas el navegador Chrome).
El lab inicia recursos y abre otra pestaña en la que se muestra la página de acceso.
Sugerencia: Ordena las pestañas en ventanas separadas, una junto a la otra.
Nota: Si ves el diálogo Elegir una cuenta, haz clic en Usar otra cuenta.
-
De ser necesario, copia el nombre de usuario a continuación y pégalo en el diálogo Acceder.
{{{user_0.username | "Username"}}}
También puedes encontrar el nombre de usuario en el panel Detalles del lab.
-
Haz clic en Siguiente.
-
Copia la contraseña que aparece a continuación y pégala en el diálogo Te damos la bienvenida.
{{{user_0.password | "Password"}}}
También puedes encontrar la contraseña en el panel Detalles del lab.
-
Haz clic en Siguiente.
Importante: Debes usar las credenciales que te proporciona el lab. No uses las credenciales de tu cuenta de Google Cloud.
Nota: Usar tu propia cuenta de Google Cloud para este lab podría generar cargos adicionales.
-
Haga clic para avanzar por las páginas siguientes:
- Acepta los Términos y Condiciones.
- No agregues opciones de recuperación o autenticación de dos factores (esta es una cuenta temporal).
- No te registres para obtener pruebas gratuitas.
Después de un momento, se abrirá la consola de Google Cloud en esta pestaña.
Nota: Para ver un menú con una lista de productos y servicios de Google Cloud, haz clic en el menú de navegación que se encuentra en la parte superior izquierda o escribe el nombre del servicio o producto en el campo Búsqueda.
Activa Google Cloud Shell
Google Cloud Shell es una máquina virtual que cuenta con herramientas para desarrolladores. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud.
Google Cloud Shell proporciona acceso de línea de comandos a tus recursos de Google Cloud.
-
En la consola de Cloud, en la barra de herramientas superior derecha, haz clic en el botón Abrir Cloud Shell.

-
Haz clic en Continuar.
El aprovisionamiento y la conexión al entorno demorarán unos minutos. Cuando te conectes, habrás completado la autenticación, y el proyecto estará configurado con tu PROJECT_ID. Por ejemplo:

gcloud es la herramienta de línea de comandos de Google Cloud. Viene preinstalada en Cloud Shell y es compatible con el completado de línea de comando.
- Puedes solicitar el nombre de la cuenta activa con este comando:
gcloud auth list
Resultado:
Credentialed accounts:
- @.com (active)
Resultado de ejemplo:
Credentialed accounts:
- google1623327_student@qwiklabs.net
- Puedes solicitar el ID del proyecto con este comando:
gcloud config list project
Resultado:
[core]
project =
Resultado de ejemplo:
[core]
project = qwiklabs-gcp-44776a13dea667a6
Nota:
La documentación completa de gcloud está disponible en la
guía de descripción general de gcloud CLI
.
Tarea 1: crear y probar una aplicación web simple con Flask para Python
En esta tarea, crearás y probarás una aplicación de Python que se usará para almacenar una lista de libros.
Nota: En la mayoría de los lenguajes, se usa sangría para que el código sea más legible. En Python, también se usa para marcar bloques de código, por lo que debe aplicarse correctamente. La cantidad de espacios que se usarán en ella debe ser coherente. Mezclar espacios y tabulaciones en la sangría también puede causar problemas. En este lab, se usan cuatro espacios para la sangría de Python.
Confirma que Cloud Shell esté autorizado
-
Para confirmar que Cloud Shell esté autorizado, en Cloud Shell, ejecuta el siguiente comando:
gcloud auth list
-
Si se te solicita que autorices Cloud Shell, haz clic en Autorizar.
Crea el directorio de apps
Para crear el directorio de apps, ejecuta el siguiente comando:
mkdir ~/bookshelf
Los archivos de la aplicación se crean en el directorio ~/bookshelf
.
Especifica y, luego, instala los requisitos
Un archivo de requisitos de Python es un archivo de texto simple en el que se detallan las dependencias que requiere tu proyecto. Para empezar, se necesitan tres módulos en un archivo de requisitos.
Nuestra app está escrita con Flask, un módulo de framework web que facilita el diseño de aplicaciones web con Python. Ejecutamos la aplicación con Gunicorn, un servidor HTTP de Python que se ejecuta en Linux. Por último, Cloud Logging se usa para registrar información desde nuestra aplicación.
-
Para crear el archivo de requisitos, ejecuta el siguiente comando:
cat > ~/bookshelf/requirements.txt <<EOF
Flask==2.3.3
gunicorn==21.2.0
google-cloud-logging==3.6.0
EOF
El archivo requirements.txt especifica las versiones de Flask, Gunicorn y Google Cloud Logging que usa la aplicación.
-
Para instalar las versiones seleccionadas de las dependencias, ejecuta el siguiente comando:
pip3 install -r ~/bookshelf/requirements.txt --user
pip es el instalador de paquetes para Python. Este comando pip3
instala los paquetes especificados en el archivo requirements.txt para usarlos con la versión 3 de Python.
Crea la implementación de la base de datos de libros
-
Para crear el código de la base de datos de libros, ejecuta el siguiente comando:
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
Los libros se almacenan en un diccionario de Python, que es una estructura de datos para almacenar pares clave-valor. La clave debe ser única, por lo que la función get_next_id()
crea un ID nuevo cada vez que se la llama.
La función read(book_id)
recupera el elemento correspondiente al book_id proporcionado.
La función create(data)
agrega el libro a la base de datos obteniendo un ID nuevo, almacenándolo en los datos del libro y, luego, almacenando la entrada de datos en el diccionario.
La función update(data, book_id)
actualiza el libro en la base de datos guardando el ID proporcionado en los datos del libro y, luego, almacenando la entrada de datos en el diccionario.
La función delete(book_id)
quita la entrada de la base de datos con la clave book_id proporcionada.
La función list()
muestra una lista de Python que contiene cada libro de la base de datos. Para recuperar esta lista, ejecuta un bucle en el diccionario y recupera cada elemento. Cada elemento almacena su ID con la clave id
.
Crea los archivos de plantillas HTML
Las plantillas son archivos que contienen datos estáticos y marcadores de posición para datos dinámicos. Renderizarás una plantilla para producir un archivo HTML final.
-
Para crear la plantilla base, ejecuta el siguiente comando:
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
Cada página de la aplicación tiene el mismo diseño básico con un cuerpo diferente. Cada una de las tres plantillas principales (lista, vista y formulario) extiende esta plantilla base especificando el contenido que se mostrará en el centro de la página.
-
Para crear la plantilla de la lista, ejecuta el siguiente comando:
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
La plantilla de lista ejecuta un bucle en lista de libros enviados a la plantilla y muestra el título, el autor y el vínculo de cada libro, que lleva al usuario a la página de visualización de ese libro.
-
Para crear la plantilla de vista, ejecuta el siguiente comando:
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
La plantilla de vista muestra los detalles del libro: título, autor, fecha de publicación y descripción. También proporciona dos botones: uno para editar el libro (que envía al usuario a la plantilla del formulario) y otro para borrar el libro (que borra el libro y devuelve al usuario a la lista de libros).
-
Para crear la plantilla del formulario, ejecuta el siguiente comando:
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
La plantilla del formulario tiene dos propósitos. Cuando se actualiza un libro, la plantilla muestra los detalles del libro actual en cuadros de edición. Cuando se hace clic en el botón Save, los campos del formulario actualizados se guardan en la base de datos.
Cuando se crea un libro nuevo, los cuadros de detalles del libro están vacíos. Cuando se hace clic en el botón Save, los datos del libro se guardan en la base de datos como un libro nuevo.
Crea el archivo de código principal
-
Para crear el archivo de código principal, ejecuta el siguiente comando:
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
main.py es el punto de entrada de la aplicación. El archivo implementa la aplicación de Flask, que especifica el enrutamiento de la URL web, renderiza las plantillas y administra la base de datos de libros. Si quieres, puedes examinar el código, que está bien comentado.
Prueba la aplicación
-
Para verificar el contenido del directorio bookshelf, ejecuta el siguiente comando:
cd ~
ls -R bookshelf
Deberías ver una lista que contiene dos archivos de Python, un archivo de requisitos y cuatro archivos de plantillas:
bookshelf:
booksdb.py main.py requirements.txt templates
bookshelf/templates:
base.html form.html list.html view.html
-
Para ejecutar el servidor HTTP de Gunicorn, ejecuta el siguiente comando:
cd ~/bookshelf; ~/.local/bin/gunicorn -b :8080 main:app
Si creaste los archivos correctamente, la aplicación debería ahora estar alojada en el puerto 8080.
-
Para ejecutar la aplicación en el navegador web, haz clic en Vista previa en la Web (Web Preview) y, luego, selecciona Preview on port 8080.

Se abre una nueva pestaña en el navegador y la aplicación está en ejecución. Esta es la URL raíz, que muestra una lista de todos los libros existentes. Aún no hay libros.
Nota: Si se te solicita que autorices Cloud Shell, haz clic en Autorizar.
-
En la pestaña de la aplicación, haz clic en +Agregar libro.
-
Ingresa un título, autor, fecha y descripción de un libro, real o imaginario, y, luego, haz clic en Guardar.
Regresarás a la página de visualización y se mostrarán los detalles de tu libro. Puedes editar o borrar tu libro haciendo clic en el botón correspondiente.
-
En la parte superior de la página, haz clic en Libros.
Regresarás a la página de visualización y se mostrarán en la lista los libros que hayas agregado.
Puedes navegar por la aplicación para agregar, editar o borrar libros si lo deseas.
-
En Cloud Shell, presiona CTRL-C
para salir de la aplicación.
Para verificar este objetivo, haz clic en Revisar mi progreso.
Crea y prueba una aplicación web simple con Flask para Python.
Tarea 2: usar Firestore para la base de datos de libros
En esta tarea, usarás una base de datos de Firestore para almacenar los datos del libro.
Actualmente, la aplicación usa un diccionario de Python en memoria. Los libros se pierden cuando la app se cierra o falla. Una mejor solución es usar Firestore para la base de datos persistente.
Crea la base de datos de Firestore
-
En el menú de navegación (
), navega a Ver todos los productos > Bases de datos.
-
Haz clic en el ícono de fijar junto a Firestore para agregarlo al menú de navegación y, luego, haz clic en Firestore.
-
Haz clic en Crear una base de datos de Firestore.
-
En Opciones de configuración, selecciona Firestore nativo.
-
En Tipo de ubicación, haz clic en Región.
No uses una multirregión para Firestore en este lab.
-
En Región, selecciona y, luego, haz clic en Crear base de datos.
Nota: Se debe seleccionar la región exacta para la ubicación. Si no ves esta región en la lista desplegable, verifica que hayas seleccionado correctamente Región, no Multirregión, en el Tipo de ubicación.
Si no hay regiones en la lista desplegable, cancela, vuelve a la página anterior y vuelve a intentar el proceso de creación de la base de datos.
La creación de la base de datos puede tardar unos minutos. Las colecciones en Firestore se crean automáticamente cuando agregas un documento a una colección, por lo que no necesitas crear una colección de libros ahora mismo.
La creación de la base de datos de Firestore habilita la API de Firestore.
Modifica la aplicación para que requiera el cliente de Python para Firestore
El cliente de Python para la API de Firestore se usa para acceder a los datos de Firestore desde tus aplicaciones. El nombre del paquete de este cliente es google-cloud-firestore y la versión que se debe usar es 2.12.0.
- Modifica la aplicación para que requiera la versión 2.12.0 del paquete google-cloud-firestore.
Nota: Puedes usar cualquier editor de archivos que desees, incluidos nano, vi y el editor de Cloud Code.
-
Para instalar la dependencia actualizada, ejecuta el siguiente comando:
pip3 install -r ~/bookshelf/requirements.txt --user
Modifica la aplicación para almacenar datos de libros en Firestore
Ahora que la aplicación requiere el paquete google-cloud-firestore, puedes usarlo en la implementación de la base de datos de libros.
Con Firestore, los datos del libro se almacenan en la base de datos de Firestore. Si tus datos tienen un campo único garantizado que se puede usar como ID, puedes elegir usarlo como ID en Firestore. En este caso, los datos que estás usando no tienen un campo único. Firestore puede crear automáticamente el ID cuando creas un libro nuevo.
Este es un ejemplo de cómo se usa el cliente de 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()
Luego, modifica la implementación de la base de datos de libros para usar Firestore.
Nota: Recuerda que se deben usar cuatro espacios para la sangría de Python.
La implementación actual usa una variable global llamada db
, que es un diccionario de Python en memoria. También usa la variable next_id
y la función get_next_id()
, que crea los IDs de los elementos almacenados en el diccionario.
Firestore administra la creación de IDs por ti. Usa una colección llamada books
. Debes agregar el ID creado al diccionario de Python que contiene los detalles de un libro antes de devolverlo al llamador.
Nota: Las pistas ocultan los cambios que debes realizar en el código. Puedes intentar escribir el código por tu cuenta o hacer clic en los botones de pistas para que se agregue el código.
-
En un editor de archivos, abre el archivo ~/bookshelf/booksdb.py
.
-
Quita las siguientes líneas del archivo:
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 datos en memoria y la funcionalidad de creación de IDs no son necesarias en esta implementación.
-
Agrega el código que importa el cliente de Firestore.
- Crea una función que convierta un documento de Firestore en un diccionario.
Cuando devuelvas un libro al llamador, no debe ser necesario que el llamador comprenda los documentos de Firestore. Las interfaces de las funciones de la base de datos de libros no deben cambiar, por lo que esta implementación sigue devolviendo y aceptando libros como diccionarios de Python.
El ID del libro debe agregarse al diccionario antes de que se muestre.
Crea una función llamada document_to_dict()
que tome un documento de Firestore como parámetro de entrada y devuelva un diccionario. El diccionario incluye los pares clave-valor en el documento y también devuelve el ID del documento como el valor de la clave id
. Si el documento no existe, debes mostrar None
.
- Modifica la función
read()
para recuperar un libro de la colección de Firestore books
. Tu función actualizada debe llamar a la función document_to_dict()
.
- Modifica la función
create()
para crear un libro en la colección de Firestore books
.
- Modifica la función
update()
para actualizar un libro en la colección de Firestore books
.
- Modifica la función
delete()
para borrar un libro en la colección de Firestore books
.
- Modifica la función
list()
para que devuelva una lista de todos los libros de la colección de Firestore books
, ordenados por título.
Listo. Cuando actualizas booksdb.py
, modificas la app para que use Firestore como base de datos sin tener que cambiar el código de llamada.
Prueba la aplicación actualizada
-
En Cloud Shell, ejecuta el siguiente comando:
cd ~/bookshelf; ~/.local/bin/gunicorn -b :8080 main:app
Si actualizaste los archivos correctamente, la aplicación debería ahora estar alojada en el puerto 8080.
Nota: Si recibes un error de importación, asegúrate de haber usado pip3 para instalar los requisitos actualizados que ahora incluyen el paquete google.cloud.firestore.
Nota: Si se te solicita que autorices Cloud Shell, haz clic en Autorizar.
-
Para ejecutar la aplicación en el navegador web, haz clic en Vista previa en la Web y, luego, selecciona Preview on port 8080.
No hay libros en la base de datos porque los libros se almacenaban en un diccionario en memoria.
-
En la pestaña de la aplicación, haz clic en +Agregar libro.
-
Ingresa la siguiente información en el formulario:
Campo |
Valor |
Title |
Hamlet |
Author |
William Shakespeare |
Date Published |
1603 |
Description |
A prince contemplates life, death, and revenge, but mostly just makes puns. |
-
Haz clic en Guardar.
Regresarás a la página de visualización y se mostrarán los detalles de tu libro.
-
En la parte superior de la página, haz clic en Libros.
Volverás a la página de la lista y verás Hamlet en ella.
Nota: Puedes agregar otros libros si quieres, pero no modifiques Hamlet, que es necesario para la siguiente tarea.
-
En el menú de navegación (
) de la consola de Google Cloud, haz clic en Firestore.
Nota: Incluso si ya estás en la página de la consola de Firestore, es posible que debas navegar a la página nuevamente para ver la base de datos.
-
Haz clic en (predeterminado).
Deberías ver que se creó un documento en la colección books para Hamlet.
-
En Cloud Shell, presiona CTRL-C
para salir de la aplicación.
Solución de problemas de la tarea 2
Los errores que se encuentran en tu aplicación se imprimen actualmente en Cloud Shell. Si tu aplicación no funciona o no guarda los datos en Firestore, usa la información de error para depurar y solucionar el problema.
Si tienes problemas para que funcione el código de la base de datos de libros, la siguiente pista proporciona un comando para reemplazar el archivo booksdb.py completo por código funcional.
Para verificar este objetivo, haz clic en Revisar mi progreso.
Usa Firestore para la base de datos de libros.
Tarea 3: usar Cloud Storage para las portadas de libros
En esta tarea, usarás Cloud Storage para almacenar las imágenes de portada de los libros.
Por lo general, una base de datos no es la ubicación adecuada para almacenar imágenes. No puedes almacenar archivos en Cloud Shell, ya que, en algún momento, querrás alojar la aplicación en otro lugar. Cloud Storage es una solución perfecta para almacenar los recursos que quieres compartir. Cloud Storage es el almacén de objetos principal de Google Cloud.
Crea el bucket de Cloud Storage
Para usar Cloud Storage, debes crear un bucket de Cloud Storage, que es un contenedor básico en el que se guardan tus datos.
-
En la consola de Google Cloud, en el menú de navegación (
), haz clic en Cloud Storage > Buckets.
-
Haz clic en +Crear.
-
Utiliza este nombre para el bucket:
{{{ project_0.project_id | project_id }}}-covers
-
Haz clic en Continuar.
-
Selecciona la Región.
-
Selecciona .
-
Haz clic en Continuar.
-
No cambies la clase de almacenamiento y haz clic en Continuar.
-
Desmarca Aplicar la prevención de acceso público a este bucket.
Dejarás el Control de acceso en Uniforme, que usa permisos a nivel de bucket para todos los objetos que se agregan al bucket.
-
Haz clic en Crear.
Para que las portadas sean visibles en la aplicación, debes permitir que todos los usuarios lean objetos dentro del bucket.
-
Selecciona la pestaña Permisos y, luego, haz clic en Otorgar acceso.
-
En Principales nuevas, ingresa allUsers.
-
Para el rol, selecciona Cloud Storage Legacy > Lector de objetos heredados de almacenamiento.
Nota: El rol Cloud Storage > Visualizador de objetos de Storage incluye el permiso para enumerar objetos en un bucket, que no es necesario para esta aplicación. El rol Cloud Storage Legacy > Lector de objetos heredados de almacenamiento solo permite la recuperación de objetos, lo que es más apropiado para este caso de uso.
-
Haz clic en Guardar.
-
Si se te solicita confirmación, haz clic en Permitir acceso público.
Actualiza el archivo de requisitos
-
En Cloud Shell, en un editor de archivos, abre el archivo ~/bookshelf/requirements.txt
.
-
En el archivo ~/bookshelf/requirements.txt
, agrega la siguiente línea:
google-cloud-storage==2.10.0
El archivo requirements.txt ahora debería verse de la siguiente manera:
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
-
Guarda el archivo.
-
Para instalar la dependencia actualizada, ejecuta el siguiente comando en Cloud Shell:
pip3 install -r ~/bookshelf/requirements.txt --user
Crea el código que subirá imágenes a Cloud Storage
El archivo storage.py
contiene código para subir una imagen de portada a Cloud Storage.
-
Para crear el archivo storage.py
, ejecuta el siguiente comando:
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 función upload_file()
acepta un flujo de archivo, un nombre de archivo y el tipo de contenido del archivo. Primero, se valida la extensión del nombre de archivo con una lista de extensiones aprobadas que se crea en un paso futuro. Luego, se agrega el nombre de archivo con la fecha y hora actuales para que las imágenes de libros que usan el mismo nombre de archivo cuando se suben no generen conflictos. El resto de la función interactúa con Cloud Storage.
El nombre del bucket se crea primero con el ID del proyecto:
bucket_name = os.getenv('GOOGLE_CLOUD_PROJECT') + '-covers'
Luego, se crea una referencia a un objeto para el bucket y el nombre de archivo especificados, y se sube el contenido del archivo de imagen:
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)
Luego, los datos del archivo se suben a Cloud Storage y el archivo se hace público para que se pueda mostrar en la aplicación web:
# upload the contents of the string into the object
blob.upload_from_string(
file_stream,
content_type=content_type)
Luego, se devuelve la URL para que se pueda almacenar en la base de datos del libro y se pueda usar para mostrar la imagen.
Modifica las plantillas para mostrar imágenes de libros
Una plantilla se renderiza con datos específicos para producir una página web.
No es necesario cambiar la plantilla base, pero las plantillas de contenido (formulario, lista, vista) deben modificarse para mostrar y subir las portadas de los libros.
-
En un editor de archivos, abre el archivo ~/bookshelf/templates/form.html
.
El formulario debe modificarse para recopilar el archivo de imagen.
-
Cerca de la parte inferior del formulario, arriba del control del botón Guardar, agrega las siguientes líneas:
<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>
La entrada image permite que el usuario suba un archivo de imagen y también muestra la imagen actual. El input imageUrl está oculto, pero almacena la URL pública de la imagen, que se agrega a la entrada de la base de datos del libro.
Ahora, el formulario debería verse de la siguiente manera:
<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>
-
Guarda el archivo.
-
En un editor de archivos, abre el archivo ~/bookshelf/templates/view.html
.
La imagen del libro debería mostrarse a la izquierda de la información del libro.
-
Después de la línea <div class="media">
, agrega las siguientes líneas:
<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>
Con esto, se agrega una nueva sección a la izquierda de los detalles del libro. Si la imagen del libro existe, se muestra en esta sección. De lo contrario, se muestra una imagen de marcador de posición.
La div media
ahora se ve de la siguiente manera:
<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>
-
Guarda el archivo.
-
En un editor de archivos, abre el archivo ~/bookshelf/templates/list.html
.
La imagen del libro ahora debería aparecer a la izquierda de cada libro de la lista.
-
Después de la línea <a href="/books/{{book.id}}">
, agrega las siguientes líneas:
<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>
Esto contiene el mismo código que agregaste a la plantilla de vista.
Modifica main.py
El archivo de código principal debe subir la imagen a Cloud Storage cuando se publique el formulario, y la URL de la imagen debe agregarse a los datos del libro.
-
En un editor de archivos, abre ~/bookshelf/main.py
.
-
Después de la importación de booksdb
, agrega la siguiente línea:
import storage
-
Después de las líneas de importación, agrega el método 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
Esta función llama a la función de la biblioteca que creaste en storage.py
para subir el archivo de portada a Cloud Storage. Devuelve la URL pública del archivo cargado.
-
Agrega la siguiente línea a la sección app.config.update
:
ALLOWED_EXTENSIONS=set(['png', 'jpg', 'jpeg', 'gif']),
Esto limita las extensiones que se permiten cuando se sube una portada de libro. Ahora, la configuración debería verse de la siguiente manera:
app.config.update(
SECRET_KEY='secret',
MAX_CONTENT_LENGTH=8 * 1024 * 1024,
ALLOWED_EXTENSIONS=set(['png', 'jpg', 'jpeg', 'gif']),
)
-
En la función add()
, después de la línea data = request.form.to_dict(flat=True)
, agrega el siguiente código:
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
Este código llama a la función upload_image_file para subir la imagen que se agregó en el formulario. También agrega la URL de la imagen a los datos del libro.
-
En la función edit()
, después de la línea data = request.form.to_dict(flat=True)
, agrega el siguiente código:
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
Este es el mismo código que agregaste a la función add()
.
-
Guarda el archivo.
Prueba la aplicación actualizada
-
En Cloud Shell, ejecuta el siguiente comando:
cd ~/bookshelf; ~/.local/bin/gunicorn -b :8080 main:app
Si actualizaste los archivos correctamente, la aplicación debería ahora estar alojada en el puerto 8080.
Nota: Si se te solicita que autorices Cloud Shell, haz clic en Autorizar.
-
Para ejecutar la aplicación en el navegador web, haz clic en Vista previa en la Web y, luego, selecciona Preview on port 8080.
Se deberían mostrar los libros que se agregaron cuando la aplicación usó Firestore. Cada libro muestra la imagen de portada de marcador de posición, ya que las URLs de las imágenes no se agregaron previamente a la base de datos.
-
Haz clic en Hamlet y, luego, en Edit book.
-
Haz clic con el botón derecho en la imagen de la portada del libro Hamlet y guárdala en tu computadora como hamlet.png:

-
En la app de Bookshelf, en Cover Image, haz clic en Choose File.
-
Selecciona el archivo que descargaste (hamlet.png) y haz clic en Abrir.
-
Haz clic en Guardar.
Ahora debería mostrarse la imagen del libro Hamlet.
-
En la consola de Google Cloud, en el menú de navegación (
), haz clic en Cloud Storage > Buckets.
-
Haz clic en el nombre del bucket (-covers).
La imagen de portada se guarda en Cloud Storage.
Para verificar este objetivo, haz clic en Revisar mi progreso.
Usa Cloud Storage para las portadas de libros.
¡Felicitaciones!
Probaste correctamente una aplicación en Cloud Shell. Modificaste la aplicación para usar las bibliotecas cliente de Cloud y almacenar sus datos en Firestore y las imágenes en Cloud Storage.
Próximos pasos/Más información
Finalice su lab
Cuando haya completado el lab, haga clic en Finalizar lab. Google Cloud Skills Boost quitará los recursos que usó y limpiará la cuenta.
Tendrá la oportunidad de calificar su experiencia en el lab. Seleccione la cantidad de estrellas que corresponda, ingrese un comentario y haga clic en Enviar.
La cantidad de estrellas indica lo siguiente:
- 1 estrella = Muy insatisfecho
- 2 estrellas = Insatisfecho
- 3 estrellas = Neutral
- 4 estrellas = Satisfecho
- 5 estrellas = Muy satisfecho
Puede cerrar el cuadro de diálogo si no desea proporcionar comentarios.
Para enviar comentarios, sugerencias o correcciones, use la pestaña Asistencia.
Copyright 2024 Google LLC. Todos los derechos reservados. Google y el logotipo de Google son marcas de Google LLC. El resto de los nombres de productos y empresas pueden ser marcas de las respectivas empresas a las que están asociados.