Usando SQLAlchemy con Flask y PostgreSQL

U

Introducción

Las bases de datos son una parte crucial de las aplicaciones modernas, ya que almacenan los datos que se utilizan para alimentarlas. Generalmente, usamos el lenguaje de consulta estructurado (SQL) para realizar consultas en la base de datos y manipular los datos dentro de ella. Aunque inicialmente se hizo a través de herramientas SQL dedicadas, rápidamente pasamos al uso de SQL desde dentro de las aplicaciones para realizar consultas.

Naturalmente, a medida que pasó el tiempo, surgieron los mapeadores relacionales de objetos (ORM), que nos permiten conectarnos de manera segura, fácil y conveniente a nuestra base de datos mediante programación sin necesidad de ejecutar consultas para manipular los datos.

Uno de esos ORM es SQLAlchemy. En esta publicación, profundizaremos en los ORM y específicamente en SQLAlchemy, luego lo usaremos para construir una aplicación web basada en bases de datos usando el Matraz marco de referencia.

¿Qué es un ORM y por qué usarlo?

El mapeo relacional de objetos, como su nombre indica, mapea objetos a entidades relacionales. En los lenguajes de programación orientados a objetos, los objetos no son tan diferentes de las entidades relacionales; tienen ciertos campos / atributos que pueden mapearse indistintamente.

Dicho esto, como es bastante fácil asignar un objeto a una base de datos, lo contrario también es muy simple. Esto facilita el proceso de desarrollo de software y reduce las posibilidades de cometer errores manuales al escribir código SQL simple.

Otra ventaja de usar ORM es que nos ayudan a escribir código que se adhiere a los principios DRY (Don’t Repeat Yourself) al permitirnos usar nuestros modelos para manipular datos en lugar de escribir código SQL cada vez que necesitamos acceder a la base de datos.

Los ORM extraen las bases de datos de nuestra aplicación, lo que nos permite utilizar varias bases de datos o cambiarlas con facilidad. Digamos, si usamos SQL en nuestra aplicación para conectarnos a una base de datos MySQL, necesitaríamos modificar nuestro código si tuviéramos que cambiar a una base de datos MSSQL ya que difieren en sintaxis.

Si nuestro SQL se integró en varios puntos de nuestra aplicación, esto resultará bastante complicado. A través de un ORM, los cambios que tendríamos que hacer se limitarían a cambiar un par de parámetros de configuración.

A pesar de que los ORM nos facilitan la vida abstrayendo las operaciones de la base de datos, debemos tener cuidado de no olvidar lo que está sucediendo bajo el capó, ya que esto también guiará cómo usamos los ORM. También necesitamos estar familiarizados con los ORM y aprenderlos para usarlos de manera más eficiente y esto introduce un poco de curva de aprendizaje.

SQLAlchemy ORM

SQLAlchemy es un ORM escrito en Python para brindar a los desarrolladores el poder y la flexibilidad de SQL, sin la molestia de usarlo realmente.

SQLAlchemy envuelve el API de base de datos de Python (Python DBAPI) que se envía con Python y fue creado para facilitar la interacción entre los módulos y las bases de datos de Python.

El DBAPI fue creado para establecer consistencia y portabilidad cuando se trata de la administración de bases de datos, aunque no necesitaremos interactuar con él directamente ya que SQLAlchemy será nuestro punto de contacto.

También es importante tener en cuenta que el ORM de SQLAlchemy está construido sobre SQLAlchemy Core – que maneja la integración DBAPI e implementa SQL. En otras palabras, SQLAlchemy Core proporciona los medios para generar consultas SQL.

Si bien SQLAlchemy ORM hace que nuestras aplicaciones sean independientes de la base de datos, es importante tener en cuenta que las bases de datos específicas requerirán controladores específicos para conectarse a ellas. Un buen ejemplo es Pyscopg que es una implementación de PostgreSQL de DBAPI que cuando se usa junto con SQLAlchemy nos permite interactuar con bases de datos de Postgres.

Para bases de datos MySQL, el PyMySQL La biblioteca ofrece la implementación de DBAPI necesaria para interactuar con ellos.

SQLAlchemy también se puede utilizar con Oracle y Microsoft SQL Server. Algunos nombres importantes de la industria que dependen de SQLAlchemy incluyen Reddit, Yelp, DropBox y Survey Monkey.

Habiendo introducido el ORM, construyamos una API Flask simple que interactúa con una base de datos de Postgres.

Matraz con SQLAlchemy

Flask es un micro-framework ligero que se utiliza para crear aplicaciones web mínimas y, a través de bibliotecas de terceros, podemos aprovechar su flexibilidad para crear aplicaciones web sólidas y ricas en funciones.

En nuestro caso, crearemos una API RESTful simple y usaremos la extensión Flask-SQLAlchemy para conectar nuestra API a una base de datos de Postgres.

Prerrequisitos

Usaremos PostgreSQL (también conocido como Postgres) para almacenar nuestros datos que serán manejados y manipulados por nuestra API.

Para interactuar con nuestra base de datos de Postgres, podemos usar la línea de comandos o clientes que vienen equipados con interfaces gráficas de usuario, lo que los hace más fáciles de usar y mucho más rápidos de navegar.

Para Mac OS, recomiendo usar Postico que es bastante simple e intuitivo y proporciona una interfaz de usuario limpia.

PgAdmin es otro excelente cliente que admite todos los principales sistemas operativos e incluso proporciona una versión Dockerized.

Usaremos estos clientes para crear la base de datos y también ver los datos durante el desarrollo y ejecución de nuestra aplicación.

Con las instalaciones fuera del camino, permítanos crear nuestro entorno e instalar las dependencias que necesitaremos para nuestra aplicación:

$ virtualenv --python=python3 env --no-site-packages
$ source env/bin/activate
$ pip install psycopg2-binary
$ pip install flask-sqlalchemy
$ pip install Flask-Migrate

Los comandos anteriores crearán y activarán un virtualenv, instalarán el controlador Psycopg2, instalarán flask-sqlalchemy e instalarán Flask-Migrate para manejar las migraciones de bases de datos.

Flask-Migrate usos Alambique, que es una herramienta ligera de migración de bases de datos que nos ayuda a interactuar con nuestra base de datos de una manera mucho más clara al ayudarnos a crear y recrear bases de datos, mover datos hacia y entre bases de datos e identificar el estado de nuestra base de datos.

En nuestro caso, no tendremos que volver a crear la base de datos o las tablas cada vez que se inicie nuestra aplicación y lo haremos automáticamente por nosotros en caso de que ninguna exista.

Implementación

Construiremos una API simple para manejar y manipular información sobre automóviles. Los datos se almacenarán en una base de datos PostgreSQL y mediante la API realizaremos operaciones CRUD.

Primero, tenemos que crear el cars_api base de datos utilizando nuestro cliente PostgreSQL de elección:

Con la base de datos en su lugar, conectemos a ella. Comenzaremos arrancando nuestra API Flask en el apps.py archivo:

from flask import Flask

app = Flask(__name__)

@app.route("https://Pharos.sh.com/")
def hello():
    return {"hello": "world"}

if __name__ == '__main__':
    app.run(debug=True)

Comenzamos creando una aplicación Flask y un único punto final que devuelve un objeto JSON.

Para nuestra demostración, usaremos Matraz-SQLAlchemy que es una extensión diseñada específicamente para agregar la funcionalidad SQLAlchemy a las aplicaciones Flask.

Ahora integremos Flask-SQLAlchemy y Flask-Migrate en nuestro app.py y crear un modelo que definirá los datos sobre nuestros coches que almacenaremos:

# Previous imports remain...
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://postgres:[email protected]:5432/cars_api"
db = SQLAlchemy(app)
migrate = Migrate(app, db)

class CarsModel(db.Model):
    __tablename__ = 'cars'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String())
    model = db.Column(db.String())
    doors = db.Column(db.Integer())

    def __init__(self, name, model, doors):
        self.name = name
        self.model = model
        self.doors = doors

    def __repr__(self):
        return f"<Car {self.name}>"

Después de importar flask_sqlalchemy, comenzamos agregando el URI de la base de datos a la configuración de nuestra aplicación. Este URI contiene nuestras credenciales, la dirección del servidor y la base de datos que usaremos para nuestra aplicación.

Luego creamos una instancia de Flask-SQLAlchemy llamada db y se utiliza para todas nuestras interacciones con la base de datos. La instancia de Flask-Migrate, llamada migrate, se crea después de eso y se utilizará para manejar las migraciones de nuestro proyecto.

los CarsModel es la clase de modelo que se utilizará para definir y manipular nuestros datos. Los atributos de la clase representan los campos que queremos almacenar en la base de datos.

Definimos el nombre de la tabla usando el __tablename__ junto a las columnas que contienen nuestros datos.

Flask se envía con una interfaz de línea de comandos y comandos dedicados. Por ejemplo, para iniciar nuestra aplicación, usamos el comando flask run. Para aprovechar este script, solo necesitamos definir una variable de entorno que especifique el script que aloja nuestra aplicación Flask:

$ export FLASK_APP=app.py
$ flask run
 * Serving Flask app "app.py" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 172-503-577

Con nuestro modelo en su lugar, y Flask-Migrate integrado, usémoslo para crear el cars tabla en nuestra base de datos:

$ flask db init
$ flask db migrate
$ flask db upgrade

Comenzamos inicializando la base de datos y habilitando las migraciones. Las migraciones generadas son solo scripts que definen las operaciones a realizar en nuestra base de datos. Como esta es la primera vez, el script generará el cars tabla con columnas como se especifica en nuestro modelo.

los flask db upgrade comando ejecuta la migración y crea nuestra tabla:

En caso de que agreguemos, eliminemos o cambiemos alguna columna, siempre podemos ejecutar el migrate y upgrade comandos para reflejar estos cambios en nuestra base de datos también.

Crear y leer entidades

Con la base de datos en su lugar y conectada a nuestra aplicación, todo lo que queda es implementar las operaciones CRUD. Empecemos por crear un car, así como recuperar todos los existentes actualmente:

# Imports and CarsModel truncated

@app.route('/cars', methods=['POST', 'GET'])
def handle_cars():
    if request.method == 'POST':
        if request.is_json:
            data = request.get_json()
            new_car = CarsModel(name=data['name'], model=data['model'], doors=data['doors'])
            db.session.add(new_car)
            db.session.commit()
            return {"message": f"car {new_car.name} has been created successfully."}
        else:
            return {"error": "The request payload is not in JSON format"}

    elif request.method == 'GET':
        cars = CarsModel.query.all()
        results = [
            {
                "name": car.name,
                "model": car.model,
                "doors": car.doors
            } for car in cars]

        return {"count": len(results), "cars": results}

Comenzamos por definir un /cars ruta que acepta tanto GET y POST peticiones. los GET solicitud devolverá una lista de todos los coches almacenados en nuestra base de datos mientras el POST recibirá los datos de un automóvil en formato JSON y completará nuestra base de datos con la información proporcionada.

Para crear un auto nuevo, usamos el CarsModel clase y proporcionar la información necesaria para completar las columnas de nuestra cars mesa. Después de crear un CarsModel objeto, creamos una sesión de base de datos y agregamos nuestro car lo.

Para guardar nuestro coche en la base de datos, confirmamos la sesión a través de db.session.commit() que cierra la transacción DB y salva nuestro coche.

Intentemos agregar un automóvil usando una herramienta como Postman:

El mensaje de respuesta nos notifica que nuestro automóvil ha sido creado y guardado en la base de datos:

Puede ver que ahora hay un registro del automóvil en nuestra base de datos.

Con los coches guardados en nuestra base de datos, el GET request nos ayudará a recuperar todos los registros. Consultamos todos los coches almacenados en nuestra base de datos utilizando el CarsModel.query.all() función, que es proporcionada por Flask-SQLAlchemy.

Esto devuelve una lista de CarsModel objetos, que luego formateamos y agregamos a una lista usando una lista de comprensión y la pasamos a la respuesta junto con el número de autos en nuestra base de datos. Cuando solicitamos la lista de autos a través de la API en Postman:

los GET método en el /cars endpoint devuelve la lista de coches tal como aparecen en nuestra base de datos, así como el recuento total.

Nota: Observe cómo no hay una sola consulta SQL presente en el código. SQLAlchemy se encarga de eso por nosotros.

Actualizar y eliminar entidades

Hasta ahora, podemos crear un solo automóvil y obtener una lista de todos los automóviles almacenados en la base de datos. Para completar el conjunto de operaciones CRUD en automóviles en nuestra API, necesitamos agregar funcionalidad para devolver los detalles, modificar y eliminar un solo automóvil.

Los métodos / verbos HTTP que usaremos para lograr esto serán GET, PUTy DELETE, que se reunirá en un único método llamado handle_car():

# Imports, Car Model, handle_cars() method all truncated

@app.route('/cars/<car_id>', methods=['GET', 'PUT', 'DELETE'])
def handle_car(car_id):
    car = CarsModel.query.get_or_404(car_id)

    if request.method == 'GET':
        response = {
            "name": car.name,
            "model": car.model,
            "doors": car.doors
        }
        return {"message": "success", "car": response}

    elif request.method == 'PUT':
        data = request.get_json()
        car.name = data['name']
        car.model = data['model']
        car.doors = data['doors']
        db.session.add(car)
        db.session.commit()
        return {"message": f"car {car.name} successfully updated"}

    elif request.method == 'DELETE':
        db.session.delete(car)
        db.session.commit()
        return {"message": f"Car {car.name} successfully deleted."}

Nuestro método handle_car() recibe el car_id de la URL y obtiene el objeto del coche tal como está almacenado en nuestra base de datos. Si el método de solicitud es GET, los detalles del coche simplemente se devolverán:

Para actualizar los detalles de nuestro coche, utilizamos el PUT método y no PATCH. Ambos métodos se pueden utilizar para actualizar los detalles, sin embargo, el PUT El método acepta una versión actualizada de nuestro recurso y reemplaza la que tenemos almacenada en la base de datos.

los PATCH El método simplemente modifica el que tenemos en nuestra base de datos sin reemplazarlo. Por lo tanto, para actualizar un CarsModel registro en nuestra base de datos, tenemos que proporcionar todos los atributos de nuestro coche, incluidos los que se van a actualizar.

Usamos los detalles para modificar nuestro objeto de automóvil y realizar estos cambios usando db.session.commit() y luego devolver una respuesta al usuario:

Nuestro coche se ha actualizado con éxito.

Por último, para borrar un coche, enviamos un DELETE solicitud al mismo punto final. Con el CarsModel objeto ya consultado, todo lo que necesitaremos hacer es usar la sesión actual para eliminarlo ejecutando db.session.delete(car) y confirmar nuestra transacción para reflejar nuestros cambios en la base de datos:

Conclusión

Las aplicaciones de la vida real no son tan simples como las nuestras y, por lo general, manejan datos relacionados y distribuidos en varias tablas.

SQLAlchemy nos permite definir relaciones y manipular datos relacionados también. Puede encontrar más información sobre el manejo de relaciones en el documentación oficial de Flask-SQLAlchemy.

Nuestra aplicación se puede ampliar fácilmente para adaptarse a las relaciones e incluso a más tablas. También podemos conectarnos a múltiples bases de datos usando Binds. Puede encontrar más información sobre Binds en el Vincula la página de documentación.

En esta publicación hemos introducido los ORM y específicamente el ORM de SQLAlchemy. Usando Flask y Flask-SQLAlchemy, hemos creado una API simple que expone y maneja datos sobre automóviles almacenados en una base de datos PostgreSQL local.

El código fuente del proyecto en esta publicación se puede encontrar en GitHub.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad