SQLAlchemy es un Object-Relational Mapper/Mapping-tool, o un ORM, es una librería que los desarrolladores utilizan para crear bases de datos y manipular sus datos sin la necesidad de conocer/usar SQL.
Existen otras alternativas en Python como Peewee, y otros lenguajes tienen sus propios ORMs, como PHP Eloquent o Java Hibernate.
Los ORM han ganado popularidad debido a que lidiar con el lenguaje SQL directamente requiere de mucho esfuerzo en la mayoría de los casos. El objetivo del ORM entonces es simplificar el mantenimiento de tus datos. Esto se hace creando objetos para tratar con las interacciones de la base de datos.
Básicamente, con ORM no tendrás que escribir SQL otra vez (el 95% del tiempo) y podrás trabajar con objetos.
Para insertar un usuario con SQL tienes que escribir:
1INSERT INTO user (name, last_name) VALUES ('Bob', 'Ross');
Con un ORM tu código sigue siendo un código familiar como este:
1user = User() 2user.name = "Bob" 3user.last_name = "Ross" 4 5# Agrega el user a la base de datos 6db.session.add(user) 7 8# Parecido al commit de Git, lo que hace esta función es guardar todos los cambios que hayas hecho 9db.session.commit()
Basta con que utilices la función db.session.commit()
y todo lo que hayas hecho con tu código se traducirá a código de lenguaje SQL.
Para utilizar SQL Alchemy necesitamos instalar la librería flask
de Python. Una vez lo hayamos hecho, estableceremos una conexión a la base de datos y definiremos el objeto db
, que es lo más importante para empezar a trabajar con ella.
1from flask import Flask 2from flask_sqlalchemy import SQLAlchemy 3 4app = Flask(__name__) 5app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:////tmp/test.db" 6db = SQLAlchemy(app)
El primer paso sería definir nuestro modelo:
1class Person(Base): 2 __tablename__ = "person" 3 4 # Aquí definimos el nombre de la tabla "Person" 5 # Ten en cuenta que cada columna es también un atributo normal de primera instancia de Python. 6 id = Column(Integer, primary_key = True) 7 name = Column(String(250), nullable = False) 8 9 # El método serialize convierte el objeto en un diccionario 10 def serialize(self): 11 return { 12 "id": self.id, 13 "name": self.name 14 }
Para insertar un registro en la base de datos, es necesario, primero, contar con la instancia que se desea añadir. A continuación, agregarlo a la sesión de la base de datos y completar la acción con un commit. En el siguiente código se visualiza esta funcionalidad (reemplaza <username_value>
y <email_value>
con los valores reales que desees agregar):
1person = Person(username = <username_value>, email = <email_value>) 2db.session.add(person) 3db.session.commit()
Hay 3 formas para obtener información de la base de datos:
MyModel.query.all()
MyModel.query.get(id)
Person.query.filter_by(arg1=value, arg2=value, ...)
1# Obtener todos los registros de una tabla/modelo en particular, en este caso, de Person 2all_people = Person.query.all() 3all_people = list(map(lambda x: x.serialize(), all_people)) 4 5# Obtener un único registro en función de su clave principal, que en este caso es el "id" de la persona (solo funciona con las claves principales) 6person = Person.query.get(3) 7 8# Obtener un grupo de registros en función de una consulta, en este caso, el string "alex" en la columna "name" 9all_people = Person.query.filter_by(name = "alex") 10all_people = list(map(lambda x: x.serialize(), all_people))
Para eliminar un registro de la base de datos es necesario seleccionar previamente la instancia que se desee borrar (a través de su clave primaria, el id) y eliminarla utilizando db.session.delete(person)
, de acuerdo al siguiente ejemplo:
1person = Person.query.get(3) 2db.session.delete(person) 3db.session.commit()
Para modificar un registro, hay que seleccionar previamente el mismo de la base de datos, luego puedes trabajar con él cambiando sus propiedades y hacer commit nuevamente, según el siguiente ejemplo:
1person = Person.query.get(3) 2person.name = "Bob" 3db.session.commit()
Una transacción es una secuencia de operaciones (como INSERT, UPDATE, SELECT) realizadas en tu base de datos. Para que una transacción esté completa, todas las operaciones deben ser exitosas. Si una operación falla, toda la transacción falla.
Todas las transacciones deben asegurar 4 propiedades principales (conocidas como propiedades ACID): Atomicidad, Consistencia, Aislamiento y Durabilidad.
Una transacción termina con COMMIT
o ROLLBACK
.
El comando COMMIT
se usa para guardar de manera permanente los cambios realizados en una transacción dentro de la base de datos.
Cuando usas INSERT, UPDATE o DELETE, los cambios realizados con estos comandos no son permanentes, los cambios hechos pueden deshacerse o, dicho con otras palabras, podemos volver atrás.
Sin embargo, cuando usas el comando COMMIT los cambios en tu base de datos serán permanentes.
El comando ROLLBACK
restaura tu base de datos hasta tu último COMMIT. También puedes usarlo con el comando SAVEPOINT para saltar a un punto que hayas guardado durante una transacción en curso.
Del mismo modo, si usas UPDATE para hacer cambios en tu base de datos, puedes deshacerlos usando el comando ROLLBACK, pero solo si aún no has usado el comando COMMIT de esta forma:
1db.session.rollback()
El comando SAVEPOINT
se usa para guardar temporalmente una transacción para así poder volver a cierto punto utilizando el comando ROLLBACK si así lo necesitas. Puedes usarlo así:
1db.session.begin_nested()
Este comando se puede llamar muchas veces, y con cada llamada se establece un punto de control llamado checkpoint
y que lleva asociado un identificador único.
Pongamos, por ejemplo, el caso de que queramos preparar una pizza y preparemos una base de datos en la que introducir los ingredientes que lleva. La base de esta pizza que queremos preparar lleva tres ingredientes: mozzarella, tomate y aceitunas. Nuestra tabla se va a llamar 'Pizza' y, después de insertar los ingredientes, se vería de la siguiente manera:
Además, tenemos una lista de ingredientes extra que podemos añadir: escogemos carne primero, pero luego cambiamos de parecer y queremos reemplazarla por champiñones. También añadiremos pepperoni y bacon. Veamos como se haría esta transacción:
1# Supongamos que ya tenemos los ingredientes base añadidos con anterioridad 2 3# Ahora insertamos un nuevo ingrediente en la pizza, la carne 4ingredient = Ingredient() 5ingredient.name = "meat" 6ingredient.id = 4 7db.session.add(ingredient) 8 9# Ahora hacemos COMMIT y lo guardamos en la base de datos, de tal forma que fijamos el ingrediente en la Pizza 10db.session.commit() 11 12# Reemplazamos el cuarto ingrediente, que antes era la carne, por los champiñones 13ingredient = Ingredient.query.get(4) 14ingredient.name = "mushrooms" 15db.session.commit() 16 17# Guardamos un "checkpoint" 18checkpoint_a = db.session.begin_nested() 19 20# Añadimos pepperoni en la pizza 21ingredient = Ingredient() 22ingredient.name = "pepperoni" 23db.session.add(ingredient) 24db.session.commit() 25 26# Un último "checkpoint" antes de añadir el bacon 27checkpoint_b = db.session.begin_nested() 28 29# Insertamos el bacon 30ingredient = Ingredient() 31ingredient.name = "bacon" 32db.session.add(ingredient)
Ahora nuestra 'Pizza' tiene los siguientes ingredientes:
Sin embargo, antes de meterla en el horno hemos decidido que no queremos bacon, así que usamos el rollback:
1checkpoint_b.rollback() 2# Vuelve atrás, hasta el checkpoint B, donde no se incluye el bacon
Finalmente, nuestra 'Pizza' se ve así:
... Me ha dado hambre después de leer esta lección ¿¿tú no tienes hambre??