Definir y acceder a la base de datos

La aplicación utilizará una base de datos SQLite para almacenar los usuarios y los mensajes. Python viene con soporte incorporado para SQLite en el módulo sqlite3.

SQLite es conveniente porque no requiere configurar un servidor de base de datos separado y está incorporado a Python. Sin embargo, si las solicitudes concurrentes intentan escribir en la base de datos al mismo tiempo, se ralentizarán ya que cada escritura se produce de forma secuencial. Las aplicaciones pequeñas no notarán esto. Una vez que se convierta en grande, es posible que desee cambiar a una base de datos diferente.

El tutorial no entra en detalles sobre SQL. Si no estás familiarizado con él, los documentos de SQLite describen el lenguaje.

Conectar con la base de datos

Lo primero que hay que hacer cuando se trabaja con una base de datos SQLite (y con la mayoría de las bibliotecas de bases de datos de Python) es crear una conexión a la misma. Todas las consultas y operaciones se realizan utilizando la conexión, que se cierra una vez finalizado el trabajo.

En las aplicaciones web esta conexión suele estar ligada a la petición. Se crea en algún momento cuando se maneja una solicitud, y se cierra antes de que se envíe la respuesta.

flaskr/db.py
import sqlite3

import click
from flask import current_app, g


def get_db():
    if 'db' not in g:
        g.db = sqlite3.connect(
            current_app.config['DATABASE'],
            detect_types=sqlite3.PARSE_DECLTYPES
        )
        g.db.row_factory = sqlite3.Row

    return g.db


def close_db(e=None):
    db = g.pop('db', None)

    if db is not None:
        db.close()

g es un objeto especial que es único para cada solicitud. Se utiliza para almacenar datos a los que podrían acceder varias funciones durante la petición. La conexión se almacena y se reutiliza en lugar de crear una nueva conexión si se llama a get_db por segunda vez en la misma petición.

current_app es otro objeto especial que apunta a la aplicación Flask que maneja la solicitud. Como has utilizado una fábrica de aplicaciones, no hay ningún objeto de aplicación cuando escribes el resto de tu código. La llamada a get_db se realizará cuando la aplicación haya sido creada y esté gestionando una petición, por lo que se puede utilizar current_app.

sqlite3.connect() establece una conexión con el fichero apuntado por la clave de configuración DATABASE. Este archivo no tiene que existir todavía, y no lo hará hasta que se inicialice la base de datos más tarde.

sqlite3.Row indica a la conexión que devuelva filas que se comporten como dicts. Esto permite acceder a las columnas por su nombre.

close_db comprueba si se ha creado una conexión comprobando si se ha establecido g.db. Si la conexión existe, se cierra. Más adelante le dirás a tu aplicación sobre la función close_db en la fábrica de la aplicación para que sea llamada después de cada petición.

Crear las tablas

En SQLite, los datos se almacenan en tablas y columnas. Estas deben ser creadas antes de poder almacenar y recuperar los datos. Flaskr almacenará los usuarios en la tabla user, y las entradas en la tabla post. Crea un archivo con los comandos SQL necesarios para crear tablas vacías:

flaskr/schema.sql
DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS post;

CREATE TABLE user (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  username TEXT UNIQUE NOT NULL,
  password TEXT NOT NULL
);

CREATE TABLE post (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  author_id INTEGER NOT NULL,
  created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  title TEXT NOT NULL,
  body TEXT NOT NULL,
  FOREIGN KEY (author_id) REFERENCES user (id)
);

Añade las funciones de Python que ejecutarán estos comandos SQL al archivo db.py:

flaskr/db.py
def init_db():
    db = get_db()

    with current_app.open_resource('schema.sql') as f:
        db.executescript(f.read().decode('utf8'))


@click.command('init-db')
def init_db_command():
    """Clear the existing data and create new tables."""
    init_db()
    click.echo('Initialized the database.')

open_resource() abre un archivo relativo al paquete flaskr, lo cual es útil ya que no necesariamente se sabrá dónde está esa ubicación cuando se despliegue la aplicación posteriormente. get_db devuelve una conexión a la base de datos, que se utiliza para ejecutar los comandos leídos del archivo.

click.command() define un comando de línea de comandos llamado init-db que llama a la función init_db y muestra un mensaje de éxito al usuario. Puedes leer Interfaz de línea de comandos para aprender más sobre la escritura de comandos.

Regístrese con la aplicación

Las funciones close_db y init_db_command necesitan ser registradas con la instancia de la aplicación; de lo contrario, no serán utilizadas por la aplicación. Sin embargo, como estás usando una función de fábrica, esa instancia no está disponible cuando escribes las funciones. En su lugar, escribe una función que tome una aplicación y haga el registro.

flaskr/db.py
def init_app(app):
    app.teardown_appcontext(close_db)
    app.cli.add_command(init_db_command)

app.teardown_appcontext() le dice a Flask que llame a esa función cuando se limpie después de devolver la respuesta.

app.cli.add_command() añade un nuevo comando que puede ser llamado con el comando flask.

Importa y llama a esta función desde la fábrica. Coloca el nuevo código al final de la función de fábrica antes de devolver la aplicación.

flaskr/__init__.py
def create_app():
    app = ...
    # existing code omitted

    from . import db
    db.init_app(app)

    return app

Inicializar el archivo de la base de datos

Ahora que init-db ha sido registrado con la aplicación, puede ser llamado usando el comando flask, similar al comando run de la página anterior.

Nota

Si todavía estás ejecutando el servidor de la página anterior, puedes parar el servidor o ejecutar este comando en un nuevo terminal. Si utilizas un nuevo terminal, recuerda cambiar al directorio de tu proyecto y activar el env como se describe en Instalación.

Ejecuta el comando init-db:

$ flask --app flaskr init-db
Initialized the database.

Ahora habrá un archivo flaskr.sqlite en la carpeta instance de tu proyecto.

Continúa con Blueprints y vistas.