Cómo usar una base de datos PostgreSQL en una aplicación Flask

El autor seleccionó el Fondo para el Software Libre y de Código Abierto para recibir una donación como parte del programa Escribe para Donaciones.

Introducción

En las aplicaciones web, generalmente necesitas una base de datos, que es una colección organizada de datos. Utilizas una base de datos para almacenar y mantener datos persistentes que se pueden recuperar y manipular de manera eficiente. Por ejemplo, en una aplicación de redes sociales, tienes una base de datos donde se almacenan los datos de los usuarios (información personal, publicaciones, comentarios, seguidores) de una manera que se puede manipular eficientemente. Puedes agregar datos a una base de datos, recuperarlos, modificarlos o eliminarlos, según diferentes requisitos y condiciones. En una aplicación web, estos requisitos pueden ser un usuario que agrega una nueva publicación, elimina una publicación o elimina su cuenta, lo que podría o no eliminar sus publicaciones. Las acciones que realices para manipular datos dependerán de las características específicas de tu aplicación. Por ejemplo, es posible que no desees que los usuarios agreguen publicaciones sin títulos.

Flask es un marco web ligero de Python que proporciona herramientas y características útiles para crear aplicaciones web en el lenguaje Python. PostgreSQL, o Postgres, es un sistema de gestión de bases de datos relacionales que proporciona una implementación del lenguaje de consulta SQL. Es compatible con los estándares y tiene muchas características avanzadas como transacciones fiables y concurrencia sin bloqueos de lectura.

En este tutorial, construirás una pequeña aplicación web de reseñas de libros que demuestra cómo usar la biblioteca psycopg2, un adaptador de base de datos PostgreSQL que te permite interactuar con tu base de datos PostgreSQL en Python. Lo usarás con Flask para realizar tareas básicas, como conectar a un servidor de base de datos, crear tablas, insertar datos en una tabla y recuperar datos de una tabla.

Requisitos previos

Paso 1 — Crear la base de datos y usuario de PostgreSQL

En este paso, creará una base de datos llamada flask_db y un usuario de base de datos llamado sammy para su aplicación Flask.

Durante la instalación de Postgres, se creó un usuario del sistema operativo llamado postgres para corresponder al usuario administrativo postgres de PostgreSQL. Necesita usar este usuario para realizar tareas administrativas. Puede usar sudo y pasar el nombre de usuario con la opción -iu.

Inicie sesión en una sesión interactiva de Postgres usando el siguiente comando:

  1. sudo -iu postgres psql

Se le proporcionará un indicador de PostgreSQL donde puede configurar sus requisitos.

Primero, cree una base de datos para su proyecto:

  1. CREATE DATABASE flask_db;

Nota: Cada declaración de Postgres debe terminar con un punto y coma, así que asegúrate de que tu comando termine con uno si estás experimentando problemas.

A continuación, crea un usuario de base de datos para nuestro proyecto. Asegúrate de seleccionar una contraseña segura:

  1. CREATE USER sammy WITH PASSWORD 'password';

Luego, otorga a este nuevo usuario acceso para administrar tu nueva base de datos:

  1. GRANT ALL PRIVILEGES ON DATABASE flask_db TO sammy;

Para confirmar que se creó la base de datos, obtén la lista de bases de datos escribiendo el siguiente comando:

  1. \l

Verás flask_db en la lista de bases de datos.

Cuando hayas terminado, sal del prompt de PostgreSQL escribiendo:

  1. \q

Ahora Postgres está configurado para que puedas conectarte y administrar su información de base de datos a través de Python utilizando la biblioteca psycopg2. A continuación, instalarás esta biblioteca junto con el paquete Flask.

Paso 2 — Instalando Flask y psycopg2

En este paso, instalarás Flask y la biblioteca psycopg2 para que puedas interactuar con tu base de datos usando Python.

Con tu entorno virtual activado, usa pip para instalar Flask y la biblioteca psycopg2:

  1. pip install Flask psycopg2-binary

Una vez que la instalación haya finalizado con éxito, verás una línea similar a la siguiente al final de la salida:

Output
Successfully installed Flask-2.0.2 Jinja2-3.0.3 MarkupSafe-2.0.1 Werkzeug-2.0.2 click-8.0.3 itsdangerous-2.0.1 psycopg2-binary-2.9.2

Ahora tienes los paquetes requeridos instalados en tu entorno virtual. A continuación, te conectarás y configurarás tu base de datos.

Paso 3 — Configuración de una Base de Datos

En este paso, creará un archivo Python en el directorio de su proyecto flask_app para conectarse a la base de datos flask_db, crear una tabla para almacenar libros e insertar algunos libros con reseñas en ella.

Primero, con su entorno de programación activado, abra un archivo nuevo llamado init_db.py en su directorio flask_app.

  1. nano init_db.py

Este archivo abrirá una conexión a la base de datos flask_db, creará una tabla llamada books y poblara la tabla utilizando datos de muestra. Agregue el siguiente código:

flask_app/init_db.py
import os
import psycopg2

conn = psycopg2.connect(
        host="localhost",
        database="flask_db",
        user=os.environ['DB_USERNAME'],
        password=os.environ['DB_PASSWORD'])

# Abrir un cursor para realizar operaciones en la base de datos
cur = conn.cursor()

# Ejecutar un comando: esto crea una nueva tabla
cur.execute('DROP TABLE IF EXISTS books;')
cur.execute('CREATE TABLE books (id serial PRIMARY KEY,'
                                 'title varchar (150) NOT NULL,'
                                 'author varchar (50) NOT NULL,'
                                 'pages_num integer NOT NULL,'
                                 'review text,'
                                 'date_added date DEFAULT CURRENT_TIMESTAMP);'
                                 )

# Insertar datos en la tabla

cur.execute('INSERT INTO books (title, author, pages_num, review)'
            'VALUES (%s, %s, %s, %s)',
            ('A Tale of Two Cities',
             'Charles Dickens',
             489,
             'A great classic!')
            )


cur.execute('INSERT INTO books (title, author, pages_num, review)'
            'VALUES (%s, %s, %s, %s)',
            ('Anna Karenina',
             'Leo Tolstoy',
             864,
             'Another great classic!')
            )

conn.commit()

cur.close()
conn.close()

Guarde y cierre el archivo.

En este archivo, primero importa el módulo os que utilizará para acceder a las variables de entorno donde almacenará su nombre de usuario y contraseña de la base de datos para que no sean visibles en su código fuente.

Importas la biblioteca psycopg2. Luego abres una conexión a la base de datos flask_db utilizando la función psycopg2.connect(). Especificas el host, que en este caso es localhost. Pasas el nombre de la base de datos al parámetro database.

Proporcionas tu nombre de usuario y contraseña a través del objeto os.environ, el cual te da acceso a las variables de entorno que configuraste en tu entorno de programación. Almacenarás el nombre de usuario de la base de datos en una variable de entorno llamada DB_USERNAME y la contraseña en una variable de entorno llamada DB_PASSWORD. Esto te permite almacenar tu nombre de usuario y contraseña fuera de tu código fuente, de modo que tu información sensible no se filtre cuando el código fuente se guarde en el control de origen o se cargue en un servidor en internet. Incluso si un atacante obtiene acceso a tu código fuente, no obtendrá acceso a la base de datos.

Creas un cursor llamado cur utilizando el método connection.cursor(), que permite que el código Python ejecute comandos PostgreSQL en una sesión de base de datos.

Utiliza el método execute() del cursor para eliminar la tabla books si ya existe. Esto evita la posibilidad de que exista otra tabla llamada books, lo que podría resultar en un comportamiento confuso (por ejemplo, si tiene columnas diferentes). Este no es el caso aquí, porque aún no has creado la tabla, por lo que el comando SQL no se ejecutará. Ten en cuenta que esto eliminará todos los datos existentes cada vez que ejecutes este archivo init_db.py. Para nuestros propósitos, solo ejecutarás este archivo una vez para iniciar la base de datos, pero es posible que desees ejecutarlo nuevamente para eliminar los datos que hayas insertado y comenzar de nuevo con los datos de muestra iniciales.

Luego utilizas CREATE TABLE books para crear una tabla llamada books con las siguientes columnas:

  • id: Un ID del tipo serial, que es un entero autoincrementable. Esta columna representa una clave primaria que especificas usando las palabras clave PRIMARY KEY. La base de datos asignará un valor único a esta clave para cada entrada.
  • title: El título del libro del tipo varchar, que es un tipo de carácter de longitud variable con un límite. varchar (150) significa que el título puede tener hasta 150 caracteres de longitud. NOT NULL significa que esta columna no puede estar vacía.
  • author: El autor del libro, con un límite de 50 caracteres. NOT NULL significa que esta columna no puede estar vacía.
  • pages_num: Un entero que representa el número de páginas que tiene el libro. NOT NULL significa que esta columna no puede estar vacía.
  • revisión: La revisión del libro. El tipo de texto significa que la revisión puede ser texto de cualquier longitud.
  • fecha_agregada: La fecha en que se agregó el libro a la tabla. DEFAULT establece el valor predeterminado de la columna en CURRENT_TIMESTAMP, que es el momento en que se agregó el libro a la base de datos. Al igual que con id, no es necesario especificar un valor para esta columna, ya que se llenará automáticamente.

Después de crear la tabla, utiliza el método execute() del cursor para insertar dos libros en la tabla, Un Cuento de Dos Ciudades de Charles Dickens, y Anna Karénina de Leo Tolstoy. Utilizas el marcador %s para pasar los valores a la declaración SQL. psycopg2 maneja la inserción en segundo plano de una manera que previene los ataques de Inyección SQL.

Una vez que termines de insertar datos de libro en tu tabla, utilizas el método connection.commit() para confirmar la transacción y aplicar los cambios a la base de datos. Luego, limpias las cosas cerrando el cursor con cur.close(), y la conexión con conn.close().

Para que se establezca la conexión con la base de datos, establece las variables de entorno DB_USERNAME y DB_PASSWORD ejecutando los siguientes comandos. Recuerda usar tu propio nombre de usuario y contraseña:

  1. export DB_USERNAME="sammy"
  2. export DB_PASSWORD="password"

Ahora, ejecuta tu archivo init_db.py en la terminal usando el comando python:

  1. python init_db.py

Una vez que el archivo finalice su ejecución sin errores, se agregará una nueva tabla books a tu base de datos flask_db.

Inicia sesión en una sesión interactiva de Postgres para revisar la nueva tabla books.

  1. sudo -iu postgres psql

Conéctate a la base de datos flask_db usando el comando \c:

  1. \c flask_db

Luego, usa una instrucción SELECT para obtener los títulos y autores de los libros de la tabla books:

  1. SELECT title, author FROM books;

Verás una salida como la siguiente:

        title         |      author
----------------------+------------------
 A Tale of Two Cities | Charles Dickens
 Anna Karenina        | Leo Tolstoy

Sal de la sesión interactiva con \q.

A continuación, crearás una pequeña aplicación Flask, te conectarás a la base de datos, recuperarás las dos reseñas de libros que insertaste en la base de datos y las mostrarás en la página de índice.

Paso 4 — Mostrar Libros

En este paso, crearás una aplicación Flask con una página de índice que recuperará los libros que están en la base de datos y los mostrará.

Con tu entorno de programación activado e Flask instalado, abre un archivo llamado app.py para editarlo dentro de tu directorio flask_app:

  1. nano app.py

Este archivo establecerá la conexión con tu base de datos y creará una sola ruta de Flask para usar esa conexión. Agrega el siguiente código al archivo:

flask_app/app.py
import os
import psycopg2
from flask import Flask, render_template

app = Flask(__name__)

def get_db_connection():
    conn = psycopg2.connect(host='localhost',
                            database='flask_db',
                            user=os.environ['DB_USERNAME'],
                            password=os.environ['DB_PASSWORD'])
    return conn


@app.route('/')
def index():
    conn = get_db_connection()
    cur = conn.cursor()
    cur.execute('SELECT * FROM books;')
    books = cur.fetchall()
    cur.close()
    conn.close()
    return render_template('index.html', books=books)

Guarda y cierra el archivo.

Aquí importas el módulo os, la biblioteca psycopg2, y la clase Flask y la función render_template() del paquete flask. Creas una instancia de la aplicación Flask llamada app.

Defines una función llamada get_db_connection(), que abre una conexión a la base de datos flask_db utilizando el usuario y la contraseña que almacenas en tus variables de entorno DB_USERNAME y DB_PASSWORD. La función devuelve el objeto de conexión conn que usarás para acceder a la base de datos.

Luego creas una ruta principal / y una función de vista index() utilizando el decorador app.route(). En la función de vista index(), abres una conexión a la base de datos usando la función get_db_connection(), creas un cursor, y ejecutas la instrucción SQL SELECT * FROM books; para obtener todos los libros que están en la base de datos. Utilizas el método fetchall() para guardar los datos en una variable llamada books. Luego cierras el cursor y la conexión. Por último, retornas una llamada a la función render_template() para renderizar un archivo de plantilla llamado index.html pasándole la lista de libros que obtuviste de la base de datos en la variable books.

Para mostrar los libros que tienes en tu base de datos en la página de índice, primero crearás una plantilla base, que tendrá todo el código HTML básico que otras plantillas también usarán para evitar la repetición de código. Luego crearás el archivo de plantilla index.html que renderizarás en tu función index(). Para obtener más información sobre plantillas, consulta Cómo Usar Plantillas en una Aplicación Flask.

Crea un directorio templates, luego abre una nueva plantilla llamada base.html:

  1. mkdir templates
  2. nano templates/base.html

Agrega el siguiente código dentro del archivo base.html:

flask_app/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %}- FlaskApp</title>
    <style>
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }

        .book {
            padding: 20px;
            margin: 10px;
            background-color: #f7f4f4;
        }

        .review {
                margin-left: 50px;
                font-size: 20px;
        }

    </style>
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">FlaskApp</a>
        <a href="#">About</a>
    </nav>
    <hr>
    <div class="content">
        {% block content %} {% endblock %}
    </div>
</body>
</html>

Guarda y cierra el archivo.

Esta plantilla base tiene todo el esquema HTML que necesitarás reutilizar en tus otras plantillas. El bloque title será reemplazado para establecer un título para cada página, y el bloque content será reemplazado con el contenido de cada página. La barra de navegación tiene dos enlaces, uno para la página de índice donde usas la función auxiliar url_for() para enlazar a la función de vista index(), y el otro para una página Acerca de si decides incluir una en tu aplicación.

A continuación, abre una plantilla llamada index.html. Esta es la plantilla a la que haces referencia en el archivo app.py:

  1. nano templates/index.html

Agrega el siguiente código a ella:

flask_app/templates/index.html

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Books {% endblock %}</h1>
    {% for book in books %}
        <div class='book'>
            <h3>#{{ book[0] }} - {{ book[1] }} BY {{ book[2] }}</h3>
            <i><p>({{ book[3] }} pages)</p></i>
            <p class='review'>{{ book[4] }}</p>
            <i><p>Added {{ book[5] }}</p></i>
        </div>
    {% endfor %}
{% endblock %}

Guarda y cierra el archivo.

En este archivo, extiendes la plantilla base y reemplazas el contenido del bloque content. Utilizas un encabezado <h1> que también sirve como título.

Utilizas un bucle Jinja for en la línea {% for book in books %} para recorrer cada libro en la lista books. Muestras el ID del libro, que es el primer elemento usando book[0]. Luego muestras el título del libro, autor, número de páginas, reseña y la fecha en que se agregó el libro.

Mientras estás en el directorio flask_app con tu entorno virtual activado, dile a Flask sobre la aplicación (app.py en este caso) usando la variable de entorno FLASK_APP. Luego establece la variable de entorno FLASK_ENV en development para ejecutar la aplicación en modo de desarrollo y tener acceso al depurador. Para obtener más información sobre el depurador de Flask, consulta Cómo manejar errores en una aplicación Flask. Usa los siguientes comandos para hacer esto:

  1. export FLASK_APP=app
  2. export FLASK_ENV=development

Asegúrate de establecer las variables de entorno DB_USERNAME y DB_PASSWORD si aún no lo has hecho:

  1. export DB_USERNAME="sammy"
  2. export DB_PASSWORD="password"

A continuación, ejecuta la aplicación:

  1. flask run

Con el servidor de desarrollo en funcionamiento, visita la siguiente URL utilizando tu navegador:

http://127.0.0.1:5000/

Verás los libros que agregaste a la base de datos en la primera inicialización.

Has mostrado los libros en tu base de datos en la página de índice. Ahora necesitas permitir a los usuarios agregar nuevos libros. Agregarás una nueva ruta para agregar libros en el siguiente paso.

Paso 5 — Agregar Libros Nuevos

En este paso, crearás una nueva ruta para agregar libros y reseñas nuevas a la base de datos.

Agregarás una página con un formulario web donde los usuarios ingresen el título del libro, el autor del libro, el número de páginas y la reseña del libro.

Deja el servidor de desarrollo ejecutándose y abre una nueva ventana de terminal.

Primero, abre tu archivo app.py:

  1. nano app.py

Para manejar el formulario web, necesitarás importar algunas cosas del paquete flask:

  • El objeto global request para acceder a los datos enviados.
  • La función url_for() para generar URL.
  • La función redirect() para redirigir a los usuarios a la página de índice después de agregar un libro a la base de datos.

Agrega estas importaciones en la primera línea del archivo:

flask_app/app.py

from flask import Flask, render_template, request, url_for, redirect

# ...

Luego agrega la siguiente ruta al final del archivo app.py:

flask_app/app.py

# ...


@app.route('/create/', methods=('GET', 'POST'))
def create():
    return render_template('create.html')

Guarda y cierra el archivo.

En esta ruta, pasas la tupla ('GET', 'POST') al parámetro methods para permitir tanto solicitudes GET como POST. Las solicitudes GET se utilizan para recuperar datos del servidor. Las solicitudes POST se utilizan para enviar datos a una ruta específica. Por defecto, solo se permiten las solicitudes GET. Cuando el usuario solicita por primera vez la ruta /create utilizando una solicitud GET, se renderizará un archivo de plantilla llamado create.html. Más tarde editarás esta ruta para manejar solicitudes POST cuando los usuarios completen y envíen el formulario web para agregar nuevos libros.

Abre la nueva plantilla create.html:

  1. nano templates/create.html

Agrega el siguiente código a ella:

flask_app/templates/create.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Add a New Book {% endblock %}</h1>
    <form method="post">
        <p>
            <label for="title">Title</label>
            <input type="text" name="title"
                   placeholder="Book title">
            </input>
        </p>

        <p>
            <label for="author">Author</label>
            <input type="text" name="author"
                   placeholder="Book author">
            </input>
        </p>

        <p>
            <label for="pages_num">Number of pages</label>
            <input type="number" name="pages_num"
                   placeholder="Number of pages">
            </input>
        </p>
        <p>
        <label for="review">Review</label>
        <br>
        <textarea name="review"
                  placeholder="Review"
                  rows="15"
                  cols="60"
                  ></textarea>
        </p>
        <p>
            <button type="submit">Submit</button>
        </p>
    </form>
{% endblock %}

Guarda y cierra el archivo.

Extiendes la plantilla base, estableces un encabezado como título y usas una etiqueta <form> con el atributo method establecido en post para indicar que el formulario enviará una solicitud POST.

Tienes un campo de texto con el nombre title, que utilizarás para acceder a los datos del título en tu ruta /create.

Tienes un campo de texto para el autor, un campo de número para el número de páginas y un área de texto para la reseña del libro.

Por último, tienes un botón Enviar al final del formulario.

Ahora, con el servidor de desarrollo en ejecución, usa tu navegador para navegar a la ruta /create:

http://127.0.0.1:5000/create

Verás una página Agregar un Nuevo Libro con un campo de entrada para el título del libro, uno para su autor, y uno para el número de páginas que tiene el libro, un área de texto para la reseña del libro, y un botón Enviar.

Si completas el formulario y lo envías, enviando una solicitud POST al servidor, no sucede nada porque no has gestionado las solicitudes POST en la ruta /create.

Abre app.py para gestionar la solicitud POST que el usuario envía:

  1. nano app.py

Edita la ruta /create para que se vea de la siguiente manera:

flask_app/app.py

 # ... 

@app.route('/create/', methods=('GET', 'POST'))
def create():
    if request.method == 'POST':
        title = request.form['title']
        author = request.form['author']
        pages_num = int(request.form['pages_num'])
        review = request.form['review']

        conn = get_db_connection()
        cur = conn.cursor()
        cur.execute('INSERT INTO books (title, author, pages_num, review)'
                    'VALUES (%s, %s, %s, %s)',
                    (title, author, pages_num, review))
        conn.commit()
        cur.close()
        conn.close()
        return redirect(url_for('index'))

    return render_template('create.html')

Guarda y cierra el archivo.

Manejas las solicitudes POST dentro de la condición if request.method == 'POST'. Extraes el título, autor, número de páginas y la reseña que el usuario envía desde el objeto request.form.

Abres una base de datos utilizando la función get_db_connection() y creas un cursor. Luego ejecutas una instrucción SQL INSERT INTO para insertar el título, autor, número de páginas y reseña que el usuario envió en la tabla books.

Confirmas la transacción y cierras el cursor y la conexión.

Por último, rediriges al usuario a la página de inicio donde pueden ver el libro recién agregado debajo de los libros existentes.

Con el servidor de desarrollo en funcionamiento, usa tu navegador para ir a la ruta /create:

http://127.0.0.1:5000/create

Completa el formulario con algunos datos y envíalo.

Serás redirigido a la página de inicio donde verás tu nueva reseña de libro.

A continuación, agregarás un enlace a la página de Crear en la barra de navegación. Abre base.html:

  1. nano templates/base.html

Edita el archivo para que se vea de la siguiente manera:

flask_app/templates/base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %} - FlaskApp</title>
    <style>
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }

        .book {
            padding: 20px;
            margin: 10px;
            background-color: #f7f4f4;
        }

        .review {
                margin-left: 50px;
                font-size: 20px;
        }

    </style>
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">FlaskApp</a>
        <a href="{{ url_for('create') }}">Create</a>
        <a href="#">About</a>
    </nav>
    <hr>
    <div class="content">
        {% block content %} {% endblock %}
    </div>
</body>
</html>

Guarda y cierra el archivo.

Aquí, agregas un nuevo enlace <a> a la barra de navegación que apunta a la página de Crear.

Actualiza tu página de índice y verás el nuevo enlace en la barra de navegación.

Ahora tienes una página con un formulario web para agregar nuevas reseñas de libros. Para obtener más información sobre formularios web, consulta Cómo Usar Formularios Web en una Aplicación Flask. Para un método más avanzado y seguro para gestionar formularios web, consulta Cómo Usar y Validar Formularios Web con Flask-WTF.

Conclusión

Has construido una pequeña aplicación web para reseñas de libros que se comunica con una base de datos PostgreSQL. Tienes funcionalidad básica de base de datos en tu aplicación Flask, como agregar nuevos datos a la base de datos, recuperar datos y mostrarlos en una página.

Si deseas leer más sobre Flask, consulta los otros tutoriales en la serie Flask.

Source:
https://www.digitalocean.com/community/tutorials/how-to-use-a-postgresql-database-in-a-flask-application