Usando SQLAlchemy con Flask y PostgreSQL

    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.

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *