SQLAlchemy en Flask

Mucha gente prefiere SQLAlchemy para el acceso a la base de datos. En este caso se recomienda utilizar un paquete en lugar de un módulo para su aplicación flask y dejar los modelos en un módulo separado (Grandes aplicaciones como paquetes). Aunque no es necesario, tiene mucho sentido.

Hay cuatro formas muy comunes de utilizar SQLAlchemy. A continuación, describiré cada una de ellas:

Extensión Flask-SQLAlchemy

Debido a que SQLAlchemy es una capa de abstracción de base de datos común y un mapeador relacional de objetos que requiere un poco de esfuerzo de configuración, hay una extensión de Flask que maneja eso por ti. Esto se recomienda si quieres empezar rápidamente.

Puedes descargar Flask-SQLAlchemy desde PyPI.

Declarativo

La extensión declarativa en SQLAlchemy es el método más reciente de utilizar SQLAlchemy. Permite definir tablas y modelos de una sola vez, de forma similar a como funciona Django. Además del siguiente texto recomiendo la documentación oficial sobre la extensión declarative.

Aquí está el módulo de ejemplo database.py para su aplicación:

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine('sqlite:////tmp/test.db')
db_session = scoped_session(sessionmaker(autocommit=False,
                                         autoflush=False,
                                         bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()

def init_db():
    # import all modules here that might define models so that
    # they will be registered properly on the metadata.  Otherwise
    # you will have to import them first before calling init_db()
    import yourapplication.models
    Base.metadata.create_all(bind=engine)

Para definir tus modelos, sólo tienes que subclasificar la clase Base que fue creada por el código anterior. Si te preguntas por qué no tenemos que preocuparnos de los hilos aquí (como hicimos en el ejemplo de SQLite3 anterior con el objeto g): es porque SQLAlchemy ya lo hace por nosotros con la clase scoped_session.

Para utilizar SQLAlchemy de forma declarativa con tu aplicación, sólo tienes que poner el siguiente código en tu módulo de aplicación. Flask eliminará automáticamente las sesiones de la base de datos al final de la solicitud o cuando la aplicación se cierre:

from yourapplication.database import db_session

@app.teardown_appcontext
def shutdown_session(exception=None):
    db_session.remove()

He aquí un modelo de ejemplo (ponlo en models.py, por ejemplo):

from sqlalchemy import Column, Integer, String
from yourapplication.database import Base

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique=True)
    email = Column(String(120), unique=True)

    def __init__(self, name=None, email=None):
        self.name = name
        self.email = email

    def __repr__(self):
        return f'<User {self.name!r}>'

Para crear la base de datos puedes utilizar la función init_db:

>>> from yourapplication.database import init_db
>>> init_db()

Puede insertar entradas en la base de datos de la siguiente manera:

>>> from yourapplication.database import db_session
>>> from yourapplication.models import User
>>> u = User('admin', 'admin@localhost')
>>> db_session.add(u)
>>> db_session.commit()

La consulta también es sencilla:

>>> User.query.all()
[<User 'admin'>]
>>> User.query.filter(User.name == 'admin').first()
<User 'admin'>

Mapeo relacional de objetos manual

El mapeo relacional de objetos manual tiene algunas ventajas y algunas desventajas frente al enfoque declarativo anterior. La principal diferencia es que se definen tablas y clases por separado y se mapean juntas. Es más flexible, pero un poco más complicado de escribir. En general, funciona como el enfoque declarativo, así que asegúrese de dividir también su aplicación en varios módulos en un paquete.

Aquí hay un ejemplo de módulo database.py para tu aplicación:

from sqlalchemy import create_engine, MetaData
from sqlalchemy.orm import scoped_session, sessionmaker

engine = create_engine('sqlite:////tmp/test.db')
metadata = MetaData()
db_session = scoped_session(sessionmaker(autocommit=False,
                                         autoflush=False,
                                         bind=engine))
def init_db():
    metadata.create_all(bind=engine)

Al igual que en el enfoque declarativo, es necesario cerrar la sesión después de cada solicitud o cierre del contexto de la aplicación. Ponga esto en su módulo de aplicación:

from yourapplication.database import db_session

@app.teardown_appcontext
def shutdown_session(exception=None):
    db_session.remove()

He aquí una tabla y un modelo de ejemplo (ponlo en models.py):

from sqlalchemy import Table, Column, Integer, String
from sqlalchemy.orm import mapper
from yourapplication.database import metadata, db_session

class User(object):
    query = db_session.query_property()

    def __init__(self, name=None, email=None):
        self.name = name
        self.email = email

    def __repr__(self):
        return f'<User {self.name!r}>'

users = Table('users', metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(50), unique=True),
    Column('email', String(120), unique=True)
)
mapper(User, users)

La consulta y la inserción funcionan exactamente igual que en el ejemplo anterior.

Capa de abstracción SQL

Si sólo quieres utilizar la capa de abstracción del sistema de base de datos (y SQL) básicamente sólo necesitas el motor:

from sqlalchemy import create_engine, MetaData, Table

engine = create_engine('sqlite:////tmp/test.db')
metadata = MetaData(bind=engine)

Entonces puedes declarar las tablas en tu código como en los ejemplos anteriores, o cargarlas automáticamente:

from sqlalchemy import Table

users = Table('users', metadata, autoload=True)

Para insertar datos se puede utilizar el método insert. Primero tenemos que obtener una conexión para poder utilizar una transacción:

>>> con = engine.connect()
>>> con.execute(users.insert(), name='admin', email='admin@localhost')

SQLAlchemy se compromete automáticamente por nosotros.

Para consultar la base de datos, se utiliza el motor directamente o se utiliza una conexión:

>>> users.select(users.c.id == 1).execute().first()
(1, 'admin', 'admin@localhost')

Estos resultados también son tuplas de tipo dictado:

>>> r = users.select(users.c.id == 1).execute().first()
>>> r['name']
'admin'

También puedes pasar cadenas de sentencias SQL al método execute():

>>> engine.execute('select * from users where id = :1', [1]).first()
(1, 'admin', 'admin@localhost')

Para obtener más información sobre SQLAlchemy, diríjase al sitio web.