Básicamente, este comando es el que variará dependiendo de si usamos sqlite3
u otro tipo de base de datos. Nada más. El resto del código permanecerá
prácticamente igual. Esta es la ventaja de usar sqlalchemy.
En este capítulo optaré por la primera opción.
Además en el programa principal vamos a crear un objeto que represente a
nuestra base de datos.
import config
from
flask_sqlalchemy import SQLAlchemy
app =
Flask(__name__)
app.config.from_object(config)
db =
SQLAlchemy(app)
@app.route('/')
def
home():
return 'esto es la página de
inicio'
@app.errorhandler(404)
def
page_not_found(error):
return "pagina no encontrada", 404
if
__name__ == '__main__':
app.run()
Diseño y creación del modelo de base de datos.
Para seguir avanzando en los conceptos de este tema vamos hacer una
pequeña y sencilla aplicación. Imaginemos que tenemos una pequeña
empresa y que necesitamos una aplicación para llevar el registro de
nuestros empleados. De cada empleado vamos a registrar:
- El nombre
- Un correo electrónico
- Un teléfono.
Un inciso.
En la parte de diseño para que no quede tan simple he utilizado
bootswatch que es como bootstrap pero con plantillas ya prediseñadas de diversos
colores. He escogido el
tema Sketchy. Esta cargado en nuestra plantilla base antes de cualquier otro
CSS.
Puedes hacerlo de dos formas:
1.- Le das al botón Download y descargas el script en local. Luego pones
donde esta el archivo en <link
rel="stylesheet" href="ruta del archivo descargado">
2.- Te pones encima del botón Download y con el botón derecho seleccionas
"Copiar dirección de enlace" y la pegas dentro de <link
rel="stylesheet" href="Pegar el enlace aquí">
Para el degradado de fondo he utilizado
uiGradients
cargado en la etiqueta <body>.
Continuamos.
Vamos a modelar la base de datos que vamos a necesitar, para que contenga
los campos (id, nombre, correo y teléfono). En
inicio.py ponemos el
código justo después de crear el objeto db y antes de las
vistas.
Vamos a comentarlo:
15 class Empleados(db.Model):
16 '''Tabla de datos de los empleados de la empresa'''
17
__tablename__ = 'empleados'
18 id = db.Column(db.Integer, primary_key=True)
19 nombre = db.Column(db.String(50), unique=True,
nullable=False)
20 correo = db.Column(db.String(30), unique=True,
nullable=False)
21 telefono = db.Column(db.Integer)
22
23 def __repr__(self):
24
return f'<Empleado {self.nombre}>'
En la línea 15 creamos una clase que heredara de
db.Model y que
llamaremos Empleados. Eso
nos permitirá modelar la base de datos. Dentro de la clase definiremos
todos los campos que están relacionados con los empleados: El id o
referencia del empleado, el nombre, correo electrónico y teléfono.
En la variable __tablename__ indicamos el nombre de la tabla a la que
corresponde esta clase. Por defecto Flask-SQLAlchemy se referirá a nuestra
tabla con el nombre de la clase en minúsculas y separando las palabras con
un guion bajo. En nuestro caso, por tanto, daría igual ponerlo que no,
pero he preferido hacerlo para que se vea como ejemplo.
Indicaremos los distintos campos que necesitaremos usando el constructor
db.Column y dentro definimos los distintos
tipos de datos
que se pueden guardar.
Entre otros puedes usar los siguientes:
- Boolean - para guardar
tipos de dato de tipo Boleano. Como valores del parámetro admite None,
True, False, 0 o 1.
- DateTime - para guardar
la fecha y hora devuelto por el modulo de python datetime.
- Float - para guardar
números de coma flotante.
- Integer - para guardar
números enteros.
- String - para guardar
cadenas de tipo string: texto, caracteres etc. Le puedes pasar como primer
parámetro el número máximo de caracteres que acepta el campo
"db.Column(db.String(max.caracteres)" o puedes conseguir lo mismo
con un parámetro usando "length = max. caracteres."
- Text - muy parecido al
anterior. No se puede especificar el tamaño máximo del campo, tiene una
longitud de string variable.
Puedes indicar que el campo tenga un valor por defecto si no se indica
otro. Por ejemplo un campo numérico que tenga un valor de 0 sería:
db.Column(db.Integer, default=0)
-
primary_key automáticamente se crea al generar un registro para diferenciar a
cada registro.
- unique o campo único que no
puede haber otro de igual contenido en la tabla.
- nullable - Si su Valor es
False obliga a que el campo siempre tenga un valor, no puede ser nulo.
- index - Si especificamos que
el campo este indexado con index=True, esto acelerará y hará que sean más
eficientes las búsquedas por ese campo.
Al final utilizamos def __repr__(self) para obtener una representación legible del objeto Empleado.
Estarás pensando...¿y donde o cuando hemos creado el archivo con la base de datos? Pues hasta ahora en ningún sitio. Solo hemos diseñado la
clase que contendrá la base de datos y creado el directorio "database".
Si recuerdas, en el archivo config.py le dijimos a la base de datos que
esta se llamaría dbase.db y estará en el directorio database al
mismo nivel que la aplicación principal. Esto lo hicimos con la
instrucción:
SQLALCHEMY_DATABASE_URI =
'sqlite:///{}/database/dbase.db'.format(PWD)
Ha llegado el momento de crear la base de datos. Para ello tenemos que
estar dentro del directorio de trabajo que contiene nuestros
archivos y ejecutar el shell de python, importar db del modulo
inicio y ejecutar db.create_all().
julian@ubuntu:~/Escritorio/Flask/14$ source
../miEntorno/bin/activate
(miEntorno) julian@ubuntu:~/Escritorio/nFlask/14$
python
Python 3.6.9 (default, Jul 17 2020, 12:50:27)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more
information.
>>> from inicio import db
>>> db.create_all()
>>>
Nota: si esta usando un entorno virtual entra primero dentro del mismo.
IMPORTANTE:
1º) Nuestra aplicación esta trabajando con SQlite, por lo que al
ejecutar el código anterior, este detecta que la base de datos no existe
aún y la crea. Sin embargo, cuando trabajes con servidores de bases de
datos como MySQL y PostgreSQL, tendrás que crear tu la base de
datos en el servidor de la base de datos.
2º) Estás instrucciones
no actualizan la estructura de la base de datos si cambiamos el
modelo. Por ejemplo si a posteriori añades un campo más a los anteriores,
por ejemplo un campo llamado "salario", tendrías que borrar las tablas
y crearlas de nuevo usando las instrucciones:
from inicio import db
db.drop_all()
db.create_all()
Podemos utilizar también la migración de base de datos que te permite,
al cambiar el modelo actualizar la estructura de la base de datos.
Para trabajar con migraciones puedes usar la extensión
Flask-Migrate.
*** Aquí voy a hacer un paréntesis en este capítulo y explicar un poco
como funciona esta extensión. Aunque en el código de esta página no lo
voy a utilizarlo, me parecía interesante verlo para futuros proyectos.
El texto del capítulo continua después de lo que está entre las dos
líneas amarillas. Si no te interesa este paquete o simplemente quieres
seguir con el capítulo sin interrupciones, puedes saltártelo.
--------------------------------------------------------------------
Como siempre, para utilizar flask-migrate, lo primero que tenemos que
hacer es entrar en nuestro entorno virtual e instalarlo con:
(venv) $ pip install flask-migrate
luego si queremos utilizarlo tenemos que importarlo y declararlo en el
archivo inicio.py de esta forma, una vez que hemos creado la
estructura de la base de datos con la clase Empleado y hemos creado
antes app y db:
Bien como nuestra aplicación inicial, no se llama app.py ni wsgi.py,
tenemos que decirle a Flask para que haga lo mostraré a continuación,
cual es nuestra aplicación principal. Para ello solo tenemos que
teclear lo siguiente:
(venv) $ export FLASK_APP=inicio.py
Esto solo dura mientras tengas la sesión abierta, si la cierras y
vuelves a entrar tendrás que teclearlo de nuevo. Aprovecho esto para
decirte que hasta ahora habíamos ejecutado el programa lanzándolo con
el interprete de python tecledando:
(venv) $ python inicio.py
Pero si declaras cual es la aplicación principal en Flask también
puedes lanzarlo con:
dicho lo cual volvemos al tema de la base de datos. La clase Empleados
que hemos creado anteriormente define la estructura inicial de nuestra
base de datos para esta aplicación. Pero cuando la aplicación siga
creciendo es muy normal que haya cambios en la misma. Podemos añadir
nuevos campos, añadir más tablas, borrar algún campo etc.
Alembic (el framework de migración utilizado por Flask-Migrate) hará
estos cambios en la estructura de la base de datos de forma que no
tengamos que volver a crearla desde cero. Pues bien, una vez que
le hemos dicho a flask cual es nuestra aplicación principal tecleamos:
(venv) $ flask db init
creating directory /14/migrations ... done
Creating directory /14/migrations/versions ... done
Generating /14/migrations/alembic.ini ... done
Generating /14/migrations/README ... done
Generating /14/migrations/env.py ... done
Generating /14/migrations/script.py.mako ... done
Please edit configuration/connection/logging settings in '/14/migrations/alembic.ini' before proceeding.
Después de haber ejecutado este comando te encontrarás un nuevo
directorio en el directorio de trabajo llamado migrations con
unos cuantos archivos y un subdirectorio llamado versions (que
contendrá las modificaciones que hagamos a la estructura de la base de
datos)
Cuando hagamos una migración, Alembic, comparará la estructura
almacenada en sus directorios con la actual que tengamos definida en
nuestro programa y realizará los cambios. Como esta es la primera vez
que ejecutaremos el comando y previamente no hay nada, lo que hará
será
crear tanto el archivo de la base de datos como su estructura.
Y lo creará en el directorio y con el nombre que hayamos especificado
dentro del archivo de configuración del proyecto, en nuestro caso en:
SQLALCHEMY_DATABASE_URI =
'sqlite:///{}/database/dbase.db'.format(PWD)
Vamos a ello:
(venv) $ flask db migrate -m "primera migracion"
/14
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added table 'empleados'
Generating /14/migrations/versions/0dd11e9faf8b_primera_migracion.py ... done
El programa acaba de generar un script para poder migrar la base de
datos con un identificador único 0dd11e9faf8b (en tu caso será otro
distinto). El comentario de la opción -m es opcional y añade una breve
descripción de texto a la migración.
Ahora podemos hacer dos cosas:
1) Aplicar la función upgrade() que aplicará la migración, con
todas sus modificaciones
2) Si nos hemos confundido o no nos interesa hacerla podemos aplicar
la función downgrade() y eliminaremos los cambios y volveremos
al punto anterior.
Como queremos consolidar los cambios utilizaremos upgrade de
esta forma:
(venv) $ flask db upgrade
/14
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> 0dd11e9faf8b, primera migracion
Y listo ya tenemos nuestra base de datos, dbasedb, con sus tabla y sus
registros preparados y listos para trabajar. En resumen, para crear o
actualizar la estructura de nuestra base de datos tenemos que primero
generar el script de migración con flask db migrate y posteriormente
consolidarla con flask db update.
El utilizar flask-migrate, es muy útil porque si por ejemplo tienes el
mismo proyecto en dos ordenadores, una vez generado el script de
migración en el primero "flask migrate", lo puedes copiar y llevar al
otro y automáticamente cuando apliques el upgrade en el segundo
consultara sus versiones, verá si la que le has puesto es la más
actual y aplicará los cambios.
--------------------------------------------------------------------
Bien, hecha la matización, volvemos a la rama principal de este
capítulo.
Si ahora miras dentro del directorio database, SORPRESA, deberías ver como se ha creado el archivo dbase.db. Por si te sirve yo estoy usando para verlo un gestor de base de
datos llamado "DB Browser for SQlite"
Pagina Principal de la Aplicación.
Lo primero que veremos nada mas entrar en la aplicación, va a ser
nuestra pantalla principal. Tendrá un aspecto como este:
La vista inicial estará formada por el siguiente código:
Asignamos todos los registros que tenemos en la base de datos a la
variable empleados con la instrucción Empleados.query.all()
y se la pasamos a la plantilla main.html.
Esta plantilla tendrá un botón en la parte superior derecha que nos
permitirá añadir un nuevo empleado. Al pulsarlo nos redirigirá a
la dirección o vista create.html.
Por otra parte, en la plantilla principal "main.html" y través
de una tabla, vamos a usar un bucle
{% for ...%}{% endfor %} para recorrer todos los registros de
la base de datos que le hemos pasado en la variable empleados, con sus
correspondientes campos (nombre, teléfono y correo electrónico)
Además como última columna añadimos dos botones para poder
"Editar" o "Borrar" cada registro. Tienes el código completo al
final del capítulo.
Añadir registros a la base de datos.
Lo primero que he hecho es crear un formulario, con los archivos
form.py y
create.htlm, donde se
recojerán los datos que necesitamos. Como ya hemos visto como se crean y
usan los formularios, no voy a pararme a explicarlo. Puedes encontrar el
código al final del capitulo y ver el post correspondiente a
crear formularios.
Quedará de la siguiente forma:
Vamos a centrarnos en la vista.
@app.route('/create/', methods=["GET", "POST"])
def create_item():
form = Crear_Registro()
if form.validate_on_submit():
# empleado es una instancia de la
clase Empleado
empleado = Empleados(
nombre=form.nombre.data,
correo=form.correo.data,
telefono=form.telefono.data)
try:
db.session.add(empleado)
db.session.commit()
return
redirect(url_for('home'))
except:
return 'ha habido un problema'
else:
return
render_template('create.html', form=form)
Creamos la ruta "/create/" y definimos la función para guardar nuestros registros create_item
Al pulsar en el botón de la pagina principal "Añadir nuevo empleado"
nos redirigirá a esta vista. Entraremos usando el método GET con lo que
se renderizará la plantilla create.html en donde le hemos pasado el
formulario.
Cuando el formulario este relleno y validado se mandará a la ruta
usando el método POST. De ahí, creamos la variable empleado, instancia
de la clase Empleados, que recogerá los valores de los campos enviados en el formulario (nombre,
correo y teléfono).
El id no lo pasamos porque automáticamente lo va a crear la base de
datos cuando añadamos los registros.
En flask-SQLAlchemy, para guardar un objeto en la base de datos, este debe
estar previamente asociado al objeto sessión. Por tanto para añadir un
nuevo empleado a la base de datos, lo haremos con
db.session.add(). Simplemente le pasamos los campos que queremos añadir que ya
están metidos en la variable empleado, en singular, que ahora mismo es un
objeto de la clase Empleados.
Bien, le hemos dicho a la base de datos que queremos añadir un registro
pero como en toda base de datos tenemos que consolidarla, sino quedará sin
hacer. Esto se consigue con la instrucción
db.session.commit().
Si todo ha ido bien volverás a la página de inicio "return redirect(url_for('home')" con el nuevo empleado ya guardado en la base de datos. Y como la
página principal se encarga de listar todos nuestros empleados este
registro se mostrará automáticamente.
Borrar registros de la base de datos.
Cada empleado en la página principal tiene un botón rojo a la derecha
que nos permitirá borrarlo de la base de datos.
Al pulsarlo te saldrá un mensaje de advertencia para confirmar la
acción.
Esto se consigue en main.html usando el atributo:
onclick="return confirm('¿Estás seguro de querer borrar el
registro?')" en la etiqueta <a> que usamos para
crear el botón.
Un punto importante es que al usar este botón, este nos
redireccione a una vista que nos permita borrar ese empleado en
concreto, para lo cual tendremos que indicar el id del mismo como
parámetro. Por ejemplo si sitúas el ratón sobre el botón
borrar del empleado cuyo Id es igual a
2, y te fijas en la parte inferior izquierda de la pantalla, veras que
nos esta indicando la url a la que nos redirigirá cuando lo
pulsemos.
Pej. 127.0.0.1:5000/delete/2/
Es decir enviamos 2 como parámetro a la URL /delete/. Para conseguir
esto, cuando en main.html diseñamos el botón de cada registro, dentro
del bucle for tenemos que utilizar:
<a href="{{url_for('delete_item', id=item.id)}}">
o
<a href="/delete/{{item.id}}/"
Para que esto funcione tenemos que
diseñar también la vista /delete/ para que acepte un parámetro,
el id, que será un número entero.
Una vez que tenemos la id del registro borrarlo es muy sencillo. Creamos
primero la función delete_item, al que tenemos que pasarle el parámetro id
y una variable empleado que recoja el resultado de buscar un
registro dentro de Empleados filtrado por su campo id y lo ordenamos que
lo borre:
empleado = Empleados.query.filter_by(id=id).delete()
y confirmamos el cambio:
db.session.commit()
Una vez hecho lo anterior volverá a la pagina principal (home) donde ser
volverán a renderizar todos los registros actualizados de la base de
datos.
Actualización de un registro existente en la base de datos.
La forma de hacerlo es parecida a la del punto anterior. Empezamos
programando el botón amarillo para editar el registro en la pantalla
principal. Al pulsarlo nos tiene que enviar a la URL /update/ enviando
como parámetro el numero de id. Para editar por ejemplo el empleado con
id=2 sería:
127.0.0.1/update/2
El código del botón quedaría en main.html como:
<a href="{{url_for('update_item', id=item.id)}}">
o
<a href="/update/{{item.id}}>
La vista en el inicio.py:
La primera vez que entramos en la vista, lo hacemos usando el metodo GET y
le pasamos el id del registro. Buscamos los datos del empleado asociado al
id enviado con:
empleado = Empleados.query.filter_by(id=id).first()
Al usar filter_by() necesitamos decirle que registro queremos
seleccionar. Aunque en este caso solo hay uno, ya que el id del empleado
es único, tenemos que especificar que el primero .first()
Luego renderizamos la plantilla 'update.html' enviando un formulario y
el id. El formulario es el mismo que creamos para introducir un nuevo
empleado con el matiz de que le pasamos, no un formulario vacío esta
vez, sino que lo rellenamos con los datos que hemos obtenido con la
consulta a la base de datos.
Ahora modificamos lo que queramos en el formulario y le damos a
enviar. El formulario se enviará a la Url anterior volviendo a
enviar de vuelta como parámetro el id y esta vez con el método POST.
<form action="{{url_for('update_item', id=id)}}"
method="post">
Como el formulario ha sido validado y enviado, el condicional
form.validate_on_submit() será verdadero y se ejecutará el código de su
interior.
Volvemos a buscar el empleado cuyo id se corresponde con el enviado por
el formulario. Esta vez lo podemos hacer de otra forma con, por
ejemplo:
empleado = Empleados.query.get(id)
e identificamos cada campo de la base de datos del empleado (nombre,
correo y teléfono), con lo obtenido del formulario.
empleado.nombre = form.nombre.data
empleado.correo = form.correo.data
empleado.telefono = form.telefono.data
y ya lo tenemos, solo nos queda consolidarlo y listo.
db.session.commit()
Volveremos a la página inicial con el dato ya modificado.
No hay comentarios:
Publicar un comentario