.. | ||
4-1a_bases_de_datos.md | ||
4-1b_database.db | ||
4-2a_structured_query_language.sql | ||
4-2b_consultas.sql | ||
4-2c_multi_table_query.sql | ||
4-2d_SQL_Joins.png | ||
4-3a_libreria_SQLite3.py | ||
4-3b_conexiones_a_bd.py | ||
4-3c_cursores.py | ||
4-3d_insert_db.py | ||
4-3d_prueba.db | ||
4-3e_select_db.py | ||
4-3f_update_db.py | ||
4-3g_delete_db.py | ||
4-4a_ORM_mapeo_relacional_de_objetos.py | ||
4-4b_add_sqlalchemy.py | ||
4-4c_query_sqlalchemy.py | ||
4-4d_objetos_relacionados.py | ||
4-4e_relaciones_entre_objetos.py | ||
4-4f_relacion_muchos_a_muchos.py | ||
4-4g_activ_sistema_escuela.py | ||
README.md |
Ir a: Repositorio, Modulo 3
Modulo 4 - Python basico
Bases de Datos
Indice
- Bases de Datos
- Structured Query Languaje
- SQLite - Manejo de BDs en Python
- ORM y SQLAlchemy
- Actividad - Sistema escuela
Bases de Datos
Por lo general un programa necesita cargar y guardar datos externos al mismo.
Si los datos son pocos y sin estructura, se pueden utilizar archivos.
Si se necesita manejar grandes volumenes de datos, y además estos datos
están relacionados, usar archivos es inviable, poco eficiente, y requieren
código mas complejo.
El tipo de base de datos mas utilizado en la actualidad son las bases
de datos relacionales (Edgar Frank Codd, IBM, sn Jose, California 1970).
Las bases de datos pueden considerarse como un conjunto de relaciones o tabas
que contienen registros o filas y cada registro contiene campos (dato especifico).
Esquemas y Datos
-
Esquema:
Definición de la estructura de la base de datos, almacena principalmente;
nombre de cada tabla, nombre de cada columna, y tipo de dato de cada columna. -
Datos:
Son el contenido de la base de datos en un momento dado
Las bases de datos relacionales utilizan lenguaje SQL (Structured Query Languaje)
Se utilizará SQLite, por ser muy liviano.
- SQLite3
- SQLiteBrowser
Funcionalidad básica SQLiteBrowser
-
Database Structure : Modificar la estructura de la base de datos
-
Browser Data : Buscar, insertar o eliminar datos en una tabla
-
Edit Pragmas : Perminte modificar parametros de la DB (opcs. avanzadas)
-
Execute SQL : Ejecutar consultas SQL
El programa permite Importar y Exportar tablas, file import/export
El modelo Relacional
Está basado en la lógica de predicados y la teoría de conjuntos.
Primary Key :
Asegura que cada registro de una tabla sea único.
No se permite tener dos registros con la misma clave primaria en una misma tabla.
La llave primaria puede ser un campo o una combinación de estos, que identifiica
cada fila de una tabla de forma única.
No pueden existir dos filas que tengan la misma clave primaria.
Son valores únicos, no nulos.
Ej. de posibles claves primarias:
- DNI - ISBM - Patente
- Nro. de tarjeta
Foreign Key :
La clave foránea identifica una columna o grupo de columnas en una tabla (tabla
hija o referendo) que se refiere a una columna o grupo de columnas en otra tabla
(tabla maestra o referenciada, puediendo ser la misma tabla).
Una clave foránea es una limitación referencial entre dos tablas.
Clave utilizada para vincular o relacionar dos tablas
Una tabla puede contener múltiples claves foráneas referenciando tablas diferentes.
La base de datos hace cumplir las restricciones de referencia.
Se debe garantizar la integridad de los datos, sus respectivas tablas,
y filas referenciadas al actualizar o eliminar.
Integridad de Datos
El sistema de gestión de base de datos relacionales, preserva la integridad
de los datos almacenados (a medida de lo posible).
Restricciones de integridad
Integridad de dominio:
Validación de las restricciones requeridas para una determinada columna de la tabla
ej.
- Datos Requeridos: valor de columna Not Null
- Validacion: Asegura que los tipos de datos insertados correspondan al especificado
Integridad de entidad:
Comprobación de unicidad del valor de la clave primaria, al insertar/modificar un registro
Integridad referencial
Asegura la integridad entre las llaves foránea y primaria. (tabla refereciada/tabla referendo)
Actualizaciones de BDs que pueden corromper la integridad referencial
Incongruencia entre clave foránea con clave primaria al insertar una fila en la tabla referendo.
Incongruencia entre clave foránea con clave primaria al actualizar la clave foránea,
a un valor inexsitente de clave primaria en la tabla refenciada.Borrando una fila referenciada. Las filas de la tabla referendo quedan huérfanas.
Actualización de clave primaria en la tabla referenciada, cuando existe(n) referencia(s)
a la fila modificada. Las filas de la tabla referendo quedan huérfanas.
Especificación SQL año 2003
Asegurando la integridad referencial
En caso de actualizar o eliminar registros de una tabla rederenciada (padre),
se definen 5 acciónes referenciales diferentes:
-
CASCADE
Cuando se elminen/actualizen las filas de una tabla padre, se eliminan/actualizan
las respectivas filas de la tabla hija. eliminacion o actualización en cascada. -
RESTRICT
Impide la modificación del valor de una columna de la tabla padre,
cuando está refereciada al valor de una fila en una tabla hija. -
NO ACTION
Similar a RESTRICT. Pero la comprobación de integridad se realiza
despues de intentar modificar la tabla (UPDATE o DELETE) -
SET DEFAULT
Cambia a un valor predefinido, el valor de referencia afectado -
SET NULL
Cambia a NULL el valor de referencia afectado.
No debe estar definido NOT NULL en la columna hija.
Indices
Estructura de datos que mejora la velocidad de las operaciones, permitiendo un
rápido acceso a los registros de una tabla, de una BD.
Pueden ser definidos como únicos, actuando como restricción,
previniendo filas idénticas en el índice.
Pueden ser creados a partir de una o más columnas.
El indice ocupa espacio en disco, por lo que puediera ralentizar un INSERT,
UPDATE o DELETE.
Detalle de implementación: Los índices son construidos generalmente sobre árboles B, B+ o B*
Structured Query Language
Diseñado para administrar y consultar sistemas de gestión de bases
de datos relacionales.
Maneja álgebra y cálculo relacional para realizar consultas.
SQL es un lenguaje de definición, manipulación y contról de datos.
Permite:
- Inserción
INSERT
- Consulta
SELECT
- Modificación
UPDATE
- Borrar
DELETE
Además de la Creación CREATE
y Modificación UPDATE
de esquemas (schemas) y control de acceso a los datos.
Tipos de datos básicos SQL
Variable Tipo | Tipo de dato |
---|---|
VARCHAR |
Cadena de caracteres |
INTEGER |
Enteros con o sin signo |
DATE |
Fecha (año, mes y día) |
TIME |
Hora (horas, minutos y segundos) |
-- Creación de una tabla
CREATE TABLE nombre_tabla (
nombre_columna1 tipo_de_dato,
nombre_columna2 tipo_de_dato,
nombre_columna3 tipo_de_dato,
....
);
Restricciones SQL
Restricción | Instrucción |
---|---|
NOT NULL |
Impide que el dato sea nulo. |
UNIQUE |
Impide que los datos se repitan. |
PRIMARY KEY |
Identifica cada registro de una tabla de forma única. |
FOREIGN KEY |
Identifica un registro de otra tabla de forma única. |
CHECK |
Asegura el cumplimiento de una condición para todos los valores en una columna. |
DEFAULT |
Valor insertado por defecto, al no especificar ninguno. |
INDEX |
Utilizados para crear y devolver datos de forma eficiente. |
Creacion de tabla
ej.
CREATE TABLE categoria_libro (
CODIGO INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
NOMBRE TEXT NOT NULL
);
-- Creación de una tabla a partir de otra
--CREATE TABLE nueva_tabla AS
-- SELECT columna1, columna2, ...
-- WHERE ...;
-- ej.
CREATE TABLE copia_libro AS
SELECT ISBN, TITULO FROM libro
Modificando el esquema de una tabla
ALTER TABLE
Agregar, eliminar o modificar, columnas en una tabla:
ALTER TABLE nombre_tabla nombre_columna tipo_de_dato
Agregar o eliminar, resticciones de columnas:
ALTER TABLE nombre_tabla ALTER COLUMN
-- Agregar una columna
-- ALTER TABLE nombre_tabla ADD nombre_columna tipo_de_dato;
--ej.
ALTER TABLE libro
ADD categoria_id INTEGER REFERENCES categoria_libro(ID);
ALTER TABLE libro
ADD precio INTEGER;
-- Borrar una columna
--ALTER TABLE nombre_tabla DROP COLUMN nombre_columna;
--ej.
ALTER TABLE copia_libro
DROP COLUMN TITULO;
-- SQlite no ofrece esta funcionalidad
-- Modificar tipo de dato de columna
--ALTER TABLE nombre_tabla ALTER COLUMN nombre_columna tipo_de_dato;
--ej.
ALTER TABLE libro
ALTER COLUMN precio REAL;
-- SQlite no ofrece esta funcionalidad
-- Agregar una restricción
ALTER TABLE libro
ADD CONSTRAINT chk_price CHECK (precio>=0);
-- SQlite no ofrece esta funcionalidad
-- Borrar tabla
--DROP TABLE nombre_tabla;
DROP TABLE copia_libro;
-- Borrar datos de una tabla
--TRUNCATE TABLE table_name;
-- SQlite no ofrece esta funcionalidad
Sentencias SQL
SELECT
Selecciona datos desde una db, estos son guardados en una tabla,
llamada conjunto de resultado. WHERE
, filtra los registros según condición dada.
--SELECT columna1, columna2, ...
--FROM nombre_tabla;
--ej.
SELECT * FROM libro;
-- * = ALL
SELECT DISTINCT
solo selecciona los valores diferentes.
--SELECT DISTINCT columna1, columna2, ...
--FROM nombre_tabla;
--ej.
SELECT DISTINCT categoria_id FROM libro;
WHERE
--SELECT columna1, columna2, ...
--FROM nombre_tabla
--WHERE condición;
--ej.
SELECT * FROM libro WHERE precio < 100;
AND
, OR
, NOT
--SELECT columna1, columna2, ...
--FROM nombre_tabla
--WHERE condición1 AND condición2 OR condición3 ...;
--ej.
SELECT * FROM libro where precio < 90 OR precio > 10 AND NOT precio = 150;
ORDER BY
--SELECT columna1, columna2, ...
--FROM nombre_tabla
--ORDER BY column2, column1, ... ASC|DESC;
--ej.
SELECT * FROM libro ORDER BY precio;
SELECT * FROM libro ORDER BY precio DESC;
LIMIT
SELECT * FROM libro LIMIT 1;
INSERT
--INSERT INTO nombre_tabla (columna1, columna2, columna3, ...)
-- VALUES (valor1, valor2, valor3, ...);
--ej.
INSERT INTO categoria_libro (NOMBRE) VALUES ('SCI');
INSERT INTO categoria_libro VALUES (4, 'Terror');
UPDATE
--UPDATE nombre_taba
--SET comumna1 = valor1, columna2 = valor2, ...
-- WHERE concición;
--ej.
UPDATE categoria_libro SET NOMBRE = 'Acción' WHERE CODIGO = 4;
DELETE
--DELETE FROM nombre_tabla WHERE condición;
--ej.
DELETE FROM categoria_libro WHERE CODIGO = 4;
--BORRAR REGISTROS DE LA TABLA
--DELETE FROM nombre_tabla
Join
LEFT JOIN
Obtener datos de varias tablas relacionadas.
--SELECT nombre_columna(s)
--FROM tabla1
--LEFT JOIN tabla2
--ON tabla1.nombre_columna = tabla2.nombre_columna;
--ej
SELECT * FROM libro LIB LEFT JOIN categoria_libro COD ON LIB.categoria_id = COD.CODIGO
INNER JOIN
Selecciona resgistros con valores coincidentes en ambas tablas.
--SELECT nombre_columna(s)
--FROM tabla1
--INNER JOIN tabla2
--ON tabla1.nombre_columna = tabla2.nombre_columna;
--ej.
SELECT * FROM libro L INNER JOIN categoria_libro C ON L.categoria_id = C.CODIGO;
Diferencias INNER JOIN
& LEFT JOIN
-
LEFT JOIN
Une la tabla de la izq. con los valores filtrados de la otra tabla. -
INNER JOIN
Filta los registros según condición, para AMBAS tablas.
--ej. agregado libro sin categoría.
INSERT INTO libro (ISBN, TITULO, DESCRIPCION, AUTOR, precio)
VALUES ('1111', 'Prueba', 'Prueba', 1, 50);
SELECT * FROM libro L LEFT JOIN categoria_libro C ON L.categoria_id = C.CODIGO;
Manejo de Bases de Datos en Python
Librería SQLite3 (parte de la biblioteca estandar de Python)
Hecha en C, provee una BD liviana basada en un archivo, se guarda en el disco,
y no requiere de un proceso aparte, permite el acceso a la BD usando una variante
no standard de lenguaje SQL.
También permite trabjabar con una base de datos en memoria. Por ejemplo.
import sqlite3
# Conexión a base de datos
conn = sqlite3.connect(':memory:')
# Cursor
cursor = conn.cursor()
# Crear tabla
cursor.execute("""CREATE TABLE moneda
(ID integer primary key, nombre text, simbolo text)""")
# Insertar datos de monedas
cursor.execute("INSERT INTO moneda VALUES (1, 'Peso (CLP)', '$')")
cursor.execute("INSERT INTO moneda VALUES (2, 'Dólar (USD)', 'U$')")
# Guardar cambios
conn.commit()
# Consultar monedas
query = "SELECT * FROM moneda"
# Obtener la respuesta (filas)
monedas = cursor.execute(query).fetchall()
print(monedas)
# Cerrar conexión a la base de datos
conn.close()
Tipos de Datos Sql-Python
Tipo Python | Tipo SQLite |
---|---|
None | Null |
int | INTEGER |
float | REAL |
str | TEXT |
bytes | BLOB |
SQLite puede almacenar tipos de datos adicionales usando adaptadores.
SQLite3 convierte tipos de SQLite a diferentes tipos de Python por medio de conversores.
Ejemplo adaptador
import sqlite3
class Point:
def __init__(self, x, y):
self.x, self.y = x,y
def __conform__(self, protocol):
if protocol is sqlite3.PrepareProtocol:
return "%f,%f" % (self.x, self.y)
con = sqlite3.connect(":memory:")
cur = con.cursor()
p = Point(5.2, -3.5)
cur.execute("select ?", (p,))
print(cur.fetchone()[0])
# Conversores y adaptadores...
Apunte migración
--Crear BD
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS 'moneda' (
'codigo' TEXT,
'nombre' text,
'symbol' text,
PRIMARY KEY('codigo')
);
COMMIT;
--Agregar campo 'factor'
BEGIN TRANSACTION;
ALTER TABLE 'moneda'
ADD 'factor' REAL;
COMMIT;
Conexiones a base de datos
import sqlite3
import hashlib
# Conexión a base de datos
conn = sqlite3.connect(':memory:')
"Cursor: Objeto cursor"
cursor = conn.cursor()
# Crear tabla
cursor.execute("""CREATE TABLE moneda
(ID integer primary key, nombre text, simbolo text)""")
"commit(): hace un commit de la transancción"
conn.commit()
excecute()
: Atajo que crea un cursor y ejecuta la consulta (metodo excecute del cursor,
devuelve el cursor), execute many, lo mismo pero con múliples consultas.
# Insertar datos de monedas
cursor.execute("INSERT INTO moneda VALUES (1, 'Peso (CLP)', '$')")
cursor.execute("INSERT INTO moneda VALUES (2, 'Dólar (USD)', 'U$')")
"rollback(): revertir cambios, al último commit"
conn.rollback()
# Consultar monedas
query = "SELECT * FROM moneda"
# Obtener la respuesta (filas)
monedas = cursor.execute(query).fetchall()
print(monedas)
# Cerrar conexión a la base de datos
conn.close()
Create Function
Permite crear una función personalizada, para ser usada dentro las sentencias SQL,
usando el nombre de la función.
Puede devolver cualquier valor soportado por SQLite:
BYTE
, STRING
, INT
, FLOAT
, NONE
.
Create Agregate
Permite crear una función de agregación, para esto de debe definir una clase
que implemente los metodos step y finalize.
finalize, devuelve el resultado de la agregación, puede devolver cualquier tipo
de dato soportado por SQLite.
Ejemplo crear función
def md5sum(t):
return hashlib.md5(t).hexdigest()
conn = sqlite3.connect(':memory:')
# create_function()
conn.create_function("md5", 1, md5sum)
cursor = conn.cursor()
# (?) es reemplazado por "a md5 hash"
cursor.execute("select md5(?)", (b"a md5 hash",))
print(cursor.fetchone()[0])
conn.close()
Ejemplo crear Agregación
class MiSum:
def __init__(self):
self.cont = 0
def step(self, valor):
self.cont += valor
def finalize(self):
return self.cont
conn = sqlite3.connect(':memory:')
conn.create_aggregate("misum", 1, MiSum)
cursor = conn.cursor()
cursor.execute("CREATE TABLE test(i)")
cursor.execute("INSERT INTO test(i) VALUES (1)")
cursor.execute("INSERT INTO test(i) VALUES (2)")
cursor.execute("SELECT MiSum(i) FROM test")
print(cursor.fetchone()[0])
conn.close()
Cursores
Estructura de contról, usada para recorrer los registros (y potencial el procesamiento)
de los registros del resultado de una consulta.
Metodos pricipales
-
execute:
Ejecuta una consulta SQL, puede estar parametrizada.
Soporta todos los tipods de 'playholders' para reemplazo
por variables. Parametros (?) y playjolders con nombre. -
execute many:
Ejecuta un conjunto de parametros personalizados. -
execute script:
Ejecuta múltiples consultas. Ejecuta un commit antes. -
fetchone:
Extrae la siguiente fila del conjunto resultado de la consulta
Devuelve una secuencia, o none si no quedan datos. -
fetchmany:
Extrae el sgte. conjunto de filas de la respuesta, devolviendo
una lista, si no hay datos, devuelve una lista vacía.
Se especifica el nro de filas a extraer o por defecto toma el
valor de la variable 'array size' del cursor. -
fetchall:
Extrae todas las filas que quedan "sin consumir", en el conjunto
resultado de la consulta. Devuelve una lista. -
close:
Cierra el cursor, queda inutilizado, Se levanta una excepción
ProgrammingError si se intenta usar. -
atributo Connection:
Solo lectura, provee la conexión a la base de datos utilizada por el cursor.
ej.
import sqlite3
# Conexión a la base de datos
conn = sqlite3.connect(":memory:")
# Cursor
cursor = conn.cursor()
# Crear tabla
cursor.execute("""CREATE TABLE moneda
(ID integer primary key, nombre text, simbolo text)""")
# Insertar datos de monedas
cursor.execute("INSERT INTO moneda VALUES (1, 'Peso (CLP)', '$')")
cursor.execute("INSERT INTO moneda VALUES (2, 'Dólar (USD)', 'U$')")
# Guardar cambios
conn.commit()
# Consultar monedas
query = "SELECT * FROM moneda"
print("Fetch one:")
# Obtener la respuesta (filas)
monedas = cursor.execute(query).fetchone()
print(monedas)
print(cursor.fetchone())
print(cursor.fetchone())
print("Fetch all:")
monedas = cursor.execute(query).fetchall()
print(monedas)
conn.close()
Guardar objeto en base de datos
Base de datos 4-3d_prueba.db
import sqlite3
DB_PATH = './4-3d_prueba.db'
class AdminMoneda(object):
def __init__(self, database=None):
if not database:
database = ':memory:'
self.conn = sqlite3.connect(database)
self.cursor = self.conn.cursor()
def insert(self, obj):
query = 'INSERT INTO moneda VALUES ("{}", "{}", "{}")'.format(obj.codigo, obj.nombre, obj.simbolo)
self.cursor.execute(query)
self.conn.commit()
class Moneda(object):
"""
Modelo Moneda
"""
objetos = AdminMoneda(DB_PATH)
def __init__(self, codigo, nombre, simbolo):
self.codigo = codigo
self.nombre = nombre
self.simbolo = simbolo
def __repr__(self):
return u'{}'.format(self.nombre)
clp = Moneda(codigo='CLP', nombre='Pesos', simbolo='$')
usd = Moneda(codigo='USD', nombre='Dolar', simbolo='US$')
eur = Moneda(codigo='USD', nombre='Dolar', simbolo='€')
Moneda.objetos.insert(clp)
Moneda.objetos.insert(usd)
Moneda.objetos.insert(eur)
Consultar Objetos
De una base de datos, desde Python
import sqlite3
DB_PATH = './4-3d_prueba.db'
class MonedaNoExiste(Exception):
pass
class AdminMoneda(object):
def __init__(self, database=None):
if not database:
database = ':memory:'
self.conn = sqlite3.connect(database)
self.cursor = self.conn.cursor()
def insert(self, obj):
query = 'INSERT INTO moneda VALUES ("{}", "{}", "{}")'.format(obj.codigo, obj.nombre, obj.simbolo)
self.cursor.execute(query)
self.conn.commit()
def get(self, codigo):
query = 'SELECT * FROM moneda WHERE codigo="{}"'.format(codigo)
self.cursor.execute(query)
datos = self.cursor.fetchone()
if not datos:
raise MonedaNoExiste("No existe la moneda código []".format(codigo))
return Moneda(codigo=datos[0], nombre=datos[1], simbolo=datos[2])
def filtro(self, **kwargs):
codigo = kwargs.get('codigo')
nombre = kwargs.get('nombre')
simbolo = kwargs.get('simbolo')
condicion = ' WHERE '
agregar_y = False
agregar_condicion = False
if codigo:
condicion += 'codigo="{}"'.format(codigo)
agregar_condicion = True
agregar_y = True
if nombre:
if agregar_y:
condicion += ' AND '
condicion += 'nombre="{}"'.format(nombre)
agregar_condicion = True
agregar_y = True
if simbolo:
if agregar_y:
condicion += ' AND '
condicion += 'simbolo="{}"'.format(simbolo)
agregar_condicion = True
query = 'SELECT * FROM moneda'
if agregar_condicion:
query += condicion
self.cursor.execute(query)
resultado = self.cursor.fetchall()
monedas = []
for datos in resultado:
moneda = Moneda(codigo=datos[0], nombre=datos[1], simbolo=datos[2])
monedas.append(moneda)
return monedas
class Moneda(object):
"""
Modelo Moneda
"""
objetos = AdminMoneda(DB_PATH)
def __init__(self, codigo, nombre, simbolo):
self.codigo = codigo
self.nombre = nombre
self.simbolo = simbolo
def __repr__(self):
return u'{}'.format(self.nombre)
ej. 4-3d
# clp = Moneda(codigo='CLP', nombre='Pesos', simbolo='$')
# usd = Moneda(codigo='USD', nombre='Dolar', simbolo='US$')
# eur = Moneda(codigo='EUR', nombre='Euro', simbolo='€')
#
# Moneda.objetos.insert(clp)
# Moneda.objetos.insert(usd)
# Moneda.objetos.insert(eur)
print(Moneda.objetos.get(codigo='CLP'))
# Moneda.objetos.get(codigo='ART')
print(Moneda.objetos.filtro(codigo='EUR'))
print(Moneda.objetos.filtro(nombre='Dolar'))
print(Moneda.objetos.filtro(simbolo='€'))
print(Moneda.objetos.filtro())
Actualizar Objetos
De una base de datos, desde Python
import sqlite3
DB_PATH = './4-3d_prueba.db'
class MonedaNoExiste(Exception):
pass
class AdminMoneda(object):
def __init__(self, database=None):
if not database:
database = ':memory:'
self.conn = sqlite3.connect(database)
self.cursor = self.conn.cursor()
def insert(self, obj):
query = 'INSERT INTO moneda VALUES ("{}", "{}", "{}")'.format(obj.codigo, obj.nombre, obj.simbolo)
self.cursor.execute(query)
self.conn.commit()
def get(self, codigo):
query = 'SELECT * FROM moneda WHERE codigo="{}"'.format(codigo)
self.cursor.execute(query)
datos = self.cursor.fetchone()
if not datos:
raise MonedaNoExiste("No existe la moneda código []".format(codigo))
return Moneda(codigo=datos[0], nombre=datos[1], simbolo=datos[2])
def filtro(self, **kwargs):
codigo = kwargs.get('codigo')
nombre = kwargs.get('nombre')
simbolo = kwargs.get('simbolo')
condicion = ' WHERE '
agregar_y = False
agregar_condicion = False
if codigo:
condicion += 'codigo="{}"'.format(codigo)
agregar_condicion = True
agregar_y = True
if nombre:
if agregar_y:
condicion += ' AND '
condicion += 'nombre="{}"'.format(nombre)
agregar_condicion = True
agregar_y = True
if simbolo:
if agregar_y:
condicion += ' AND '
condicion += 'simbolo="{}"'.format(simbolo)
agregar_condicion = True
query = 'SELECT * FROM moneda'
if agregar_condicion:
query += condicion
self.cursor.execute(query)
resultado = self.cursor.fetchall()
monedas = []
for datos in resultado:
moneda = Moneda(codigo=datos[0], nombre=datos[1], simbolo=datos[2])
monedas.append(moneda)
return monedas
def update(self, old_obj, obj):
updated = False
add_comma = False
query = 'UPDATE moneda SET '
if old_obj.nombre != obj.nombre:
query += 'nombre="{}"'.format(obj.nombre)
updated = True
add_comma = True
if old_obj.simbolo != obj.simbolo:
if add_comma:
query += ', '
query += 'simobolo="{}"'.format(obj.simbolo)
if updated:
query += ' WHERE codigo="{}"'.format(obj.codigo)
self.cursor.execute(query)
self.conn.commit()
def save(self, obj):
try:
old_obj = self.get(codigo=obj.codigo)
except MonedaNoExiste:
self.insert(obj)
else:
self.update(old_obj, obj)
class Moneda(object):
"""
Modelo Moneda
"""
objetos = AdminMoneda(DB_PATH)
def __init__(self, codigo, nombre, simbolo):
self.codigo = codigo
self.nombre = nombre
self.simbolo = simbolo
def __repr__(self):
return u'{}'.format(self.nombre)
ej. 4-3d
# clp = Moneda(codigo='CLP', nombre='Pesos', simbolo='$')
# usd = Moneda(codigo='USD', nombre='Dolar', simbolo='US$')
# eur = Moneda(codigo='EUR', nombre='Euro', simbolo='€')
#
# Moneda.objetos.insert(clp)
# Moneda.objetos.insert(usd)
# Moneda.objetos.insert(eur)
ej. 4-3e
# print(Moneda.objetos.get(codigo='CLP'))
#
# Moneda.objetos.get(codigo='ART')
# print(Moneda.objetos.filtro(codigo='EUR'))
# print(Moneda.objetos.filtro(nombre='Dolar'))
# print(Moneda.objetos.filtro(simbolo='€'))
#
# print(Moneda.objetos.filtro())
peso_cl = Moneda.objetos.get(codigo='CLP')
Moneda.objetos.save(peso_cl)
print(peso_cl.codigo)
print(peso_cl.nombre)
print(peso_cl.simbolo)
peso_cl.nombre = 'Pesos (CL)'
Moneda.objetos.save(peso_cl)
peso_cl = Moneda.objetos.get(codigo='CLP')
print(peso_cl.codigo)
print(peso_cl.nombre)
print(peso_cl.simbolo)
peso_uru = Moneda(codigo='UYU', nombre='Pesos Uruguayos', simbolo='$')
Moneda.objetos.save(peso_uru)
pesos_uru = Moneda.objetos.get(codigo='UYU')
print(peso_uru.codigo)
print(peso_uru.nombre)
print(peso_uru.simbolo)
Borrar Objetos
De una base de datos, desde Python
import sqlite3
DB_PATH = './4-3d_prueba.db'
class MonedaNoExiste(Exception):
pass
class AdminMoneda(object):
def __init__(self, database=None):
if not database:
database = ':memory:'
self.conn = sqlite3.connect(database)
self.cursor = self.conn.cursor()
def insert(self, obj):
query = 'INSERT INTO moneda VALUES ("{}", "{}", "{}")'.format(obj.codigo, obj.nombre, obj.simbolo)
self.cursor.execute(query)
self.conn.commit()
def get(self, codigo):
query = 'SELECT * FROM moneda WHERE codigo="{}"'.format(codigo)
self.cursor.execute(query)
datos = self.cursor.fetchone()
if not datos:
raise MonedaNoExiste("No existe la moneda código []".format(codigo))
return Moneda(codigo=datos[0], nombre=datos[1], simbolo=datos[2])
def filtro(self, **kwargs):
codigo = kwargs.get('codigo')
nombre = kwargs.get('nombre')
simbolo = kwargs.get('simbolo')
condicion = ' WHERE '
agregar_y = False
agregar_condicion = False
if codigo:
condicion += 'codigo="{}"'.format(codigo)
agregar_condicion = True
agregar_y = True
if nombre:
if agregar_y:
condicion += ' AND '
condicion += 'nombre="{}"'.format(nombre)
agregar_condicion = True
agregar_y = True
if simbolo:
if agregar_y:
condicion += ' AND '
condicion += 'simbolo="{}"'.format(simbolo)
agregar_condicion = True
query = 'SELECT * FROM moneda'
if agregar_condicion:
query += condicion
self.cursor.execute(query)
resultado = self.cursor.fetchall()
monedas = []
for datos in resultado:
moneda = Moneda(codigo=datos[0], nombre=datos[1], simbolo=datos[2])
monedas.append(moneda)
return monedas
def update(self, old_obj, obj):
updated = False
add_comma = False
query = 'UPDATE moneda SET '
if old_obj.nombre != obj.nombre:
query += 'nombre="{}"'.format(obj.nombre)
updated = True
add_comma = True
if old_obj.simbolo != obj.simbolo:
if add_comma:
query += ', '
query += 'simobolo="{}"'.format(obj.simbolo)
if updated:
query += ' WHERE codigo="{}"'.format(obj.codigo)
self.cursor.execute(query)
self.conn.commit()
def save(self, obj):
try:
old_obj = self.get(codigo=obj.codigo)
except MonedaNoExiste:
self.insert(obj)
else:
self.update(old_obj, obj)
def delete(self, obj):
query = 'DELETE FROM moneda WHERE codigo="{}"'.format(obj.codigo)
self.cursor.execute(query)
self.conn.commit()
class Moneda(object):
"""
Modelo Moneda
"""
objetos = AdminMoneda(DB_PATH)
def __init__(self, codigo, nombre, simbolo):
self.codigo = codigo
self.nombre = nombre
self.simbolo = simbolo
def __repr__(self):
return u'{}'.format(self.nombre)
ej. 4-3d
# clp = Moneda(codigo='CLP', nombre='Pesos', simbolo='$')
# usd = Moneda(codigo='USD', nombre='Dolar', simbolo='US$')
# eur = Moneda(codigo='EUR', nombre='Euro', simbolo='€')
#
# Moneda.objetos.insert(clp)
# Moneda.objetos.insert(usd)
# Moneda.objetos.insert(eur)
ej. 4-3e
# print(Moneda.objetos.get(codigo='CLP'))
#
# Moneda.objetos.get(codigo='ART')
# print(Moneda.objetos.filtro(codigo='EUR'))
# print(Moneda.objetos.filtro(nombre='Dolar'))
# print(Moneda.objetos.filtro(simbolo='€'))
#
# print(Moneda.objetos.filtro())
ej. 4-3f
# peso_cl = Moneda.objetos.get(codigo='CLP')
# Moneda.objetos.save(peso_cl)
#
# print(peso_cl.codigo)
# print(peso_cl.nombre)
# print(peso_cl.simbolo)
#
# peso_cl.nombre = 'Pesos (CL)'
# Moneda.objetos.save(peso_cl)
# peso_cl = Moneda.objetos.get(codigo='CLP')
# print(peso_cl.codigo)
# print(peso_cl.nombre)
# print(peso_cl.simbolo)
#
# peso_uru = Moneda(codigo='UYU', nombre='Pesos Uruguayos', simbolo='$')
# Moneda.objetos.save(peso_uru)
#
# pesos_uru = Moneda.objetos.get(codigo='UYU')
# print(peso_uru.codigo)
# print(peso_uru.nombre)
# print(peso_uru.simbolo)
pesos_uru = Moneda.objetos.get(codigo='UYU')
Moneda.objetos.delete(pesos_uru)
# pesos_uru = Moneda.objetos.get(codigo='UYU')
ORM
Object Relational Mapping Mapeo Relacional de Objetos
Técnica de programación para convertir tipos de datos utilizados entre un lenguaje
de POO y una base de datos relacional como motor de persistencia.
Hay librerías que desarrollan el mapeo relacional de objetos, SQLAlchemy es uno de
los ORM más utilizados en Python.
Tambíen es posible crear propias herramientas ORM.
ORM permite cambiar de motor de DB mapeando automáticamente, ademas de armar
las consultas SQL, evitando así el uso del lenguaje SQL.
Instalación: $ pip install SQLAlchemy
Mapeo de los Modelos
Usando el ORM de SQLAlchemy.
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Sequence
engine = create_engine('sqlite:///:memory:')
Base = declarative_base()
class Author(Base):
__tablename__ = 'author'
id = Column(Integer, Sequence('author_id_seq'), primary_key=True)
firstname = Column(String)
lastname = Column(String)
def __repr__(self):
return "{} {}".format(self.firstname, self.lastname)
print(Author.__table__, end='\n'*2)
# Creacción del esquema
Base.metadata.create_all(engine)
author = Author(firstname="Juanita", lastname='Leon')
print(author.firstname)
print(author.lastname)
print(author.id)
$ python -i
Python 3.8.2 (default, Jul 16 2020, 14:00:26)
[GCC 9.3.0] on linux
>>> runfile('./4-3h_ORM_mapeo_relacional_de_objetos.py', wdir='./')
Author.__table__
Table('author', MetaData(bind=None), Column('id', Integer(), table=<author>,
primary_key=True, nullable=False, default=Sequence('author_id_seq',
metadata=MetaData(bind=None))), Column('firstname', String(), table=<author>),
Column('lastname', String(), table=<author>), schema=None)
author.firstname
'Juanita'
author.lastname
'Leon'
author.id
Guardar Objetos
En la base de datos con ORM de SQLAlchemy
Mapeo de los Modelos
Con la base de datos, usando el ORM de SQLAlchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Sequence
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class Author(Base):
__tablename__ = 'author'
id = Column(Integer, Sequence('author_id_seq'), primary_key=True)
firstname = Column(String)
lastname = Column(String)
def __repr__(self):
return "{} {}".format(self.firstname, self.lastname)
Creacción del esquema CREATE_ALL
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
Agregando objetos a DB, flush automatico al hacer una consulta
posterior a SESSION.ADD
author = Author(firstname="Juanita", lastname='Leon')
session.add(author)
our_author = session.query(Author).filter_by(firstname='Juanita').first()
# son la misma instancia
print(author is our_author, '\n')
Agregar lista de objetos a la DB, SESION.ADD_ALL
session.add_all([Author(firstname='Joél Ez', lastname='Silva'),
Author(firstname='Jorge', lastname='Olivares')])
SESSION.NEW
Aun no estan guardados en la base, pero están en la sesión.
print(session.new)
# Modificando objeto en sesión, ver con SESSION.DIRTY
author.firstname = 'Anita'
print(session.dirty, '\n')
SESSION.COMMIT
Enviar todos los cambios a la BD. Se devuelve la conexión al pull de conexiones.
Una vez insertados. Todos identificadores y valores por defecto generados por
la BD están disponibles en instancia.
session.commit()
print(author.id, '\n')
ROLLBACK
Podemos hacer rollback de todos los cambios no incluidos en un commit.
author.firstname = 'Ruben'
another_author = Author(firstname='Uriel', lastname='Alelias')
session.add(another_author)
res = session.query(Author).filter(Author.firstname.in_(['Ruben', 'Uriel'])).all()
print(res, '\n')
session.rollback()
print(author.firstname)
# another_author no está en sesión, producto del rollback
print(another_author in session)
Consultar objetos SQLAlchemy
De la base de datos, con ORM de SQLAlchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Sequence
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import aliased
engine = create_engine('sqlite:///:memory:')
Base = declarative_base()
class Author(Base):
__tablename__ = 'author'
id = Column(Integer, Sequence('author_id_seq'), primary_key=True)
firstname = Column(String)
lastname = Column(String)
def __repr__(self):
return "{} {}".format(self.firstname, self.lastname)
Base.metadata.create_all(engine)
author = Author(firstname="Juanita", lastname='Leon')
Session = sessionmaker(bind=engine)
session = Session()
session.add(author)
session.add_all([Author(firstname='Joél Ez', lastname='Silva'),
Author(firstname='Jorge', lastname='Olivares')])
session.commit()
Querys SQLAlchemy
Query #1: Devuelve los Autores ordenados por ID.
for instance in session.query(Author).order_by(Author.id):
print(instance.firstname, instance.lastname)
# Juanita Leon
# Joél Ez Silva
# Jorge Olivares
Query #2: Devuelve el Nombre y Apellido de cada Autor.
for firstname, lastname in session.query(Author.firstname, Author.lastname):
print(firstname, lastname)
# Juanita Leon
# Joél Ez Silva
# Jorge Olivares
Query #3: Devuelve el Autor, y su primer nombre.
for row in session.query(Author, Author.firstname).all():
print(row.Author, row.firstname)
# Juanita Leon Juanita
# Joél Ez Silva Joél Ez
# Jorge Olivares Jorge
Query #4: Devuelve los Autores, asignando una etiqueta al campo firstname.
for row in session.query(Author.firstname.label('firstname_label')).all():
print(row.firstname_label)
# Juanita
# Joél Ez
# Jorge
Query #5: Devuelve el Autor y su primer nombre, definiendo un alias de la tabla.
author_alias = aliased(Author, name='author_alias')
for row in session.query(author_alias, author_alias.firstname).all():
print(row.author_alias)
# Juanita Leon
# Joél Ez Silva
# Jorge Olivares
Query #6: Busqueda de todos los Autores, filtrados por slices, del 2 al 3.
for an_author in session.query(Author).order_by(Author.id)[1:3]:
print(an_author)
# Joél Ez Silva
# Jorge Olivares
Query #7: Filtrado por autores con nombre 'Joél Ez'
for name, in session.query(Author.firstname).filter_by(firstname='Joél Ez'):
print(name)
# Joél Ez
Query #8 Filtrado por apellido del Autor.
for name, in session.query(Author.firstname).filter(Author.lastname == 'Silva'):
print(name)
# Joél Ez
Query #9: Filtrado por nombre de autor y apellido.
for an_author in session.query(Author).\
filter(Author.firstname == 'Jorge').\
filter(Author.lastname == 'Olivares'):
print(an_author)
# Jorge Olivares
Query #10: Cantidad de autores con el nombre 'Juanita'.
print(session.query(Author).filter(Author.firstname == 'Juanita').count())
# 1
Construyendo relación entre objetos
- Crear columna clave foránea hacia la tabla del modelo a relacionar.
- Crear relación con el modelo utilizando el constructor de relaciones llamado relationship.
- La relación es bidireccional.
Ej. relación uno a muchos (un autor muchos libros)
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Sequence, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship
Base = declarative_base()
class Author(Base):
__tablename__ = 'author'
id = Column(Integer, Sequence('author_id_seq'), primary_key=True)
firstname = Column(String)
lastname = Column(String)
# books = relationship("Book", order_by="Book.id", back_populates="author")
# CASCADE
books = relationship("Book", order_by="Book.id", back_populates="author",
cascade="all, delete, delete-orphan")
def __repr__(self):
return "{} {}".format(self.firstname, self.lastname)
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, Sequence('book_id_seq'), primary_key=True)
isbn = Column(String)
title = Column(String)
description = Column(String)
author_id = Column(Integer, ForeignKey('author.id'))
author = relationship("Author", back_populates="books")
def __repr__(self):
return "{}".format(self.title)
Trabajando con Objetos Relacionados se puede solicitar libros por autor o al autor por sus libros.
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
# crea un autor
j_rowling = Author(firstname='Joanne', lastname='Rowling')
j_rowling.books = [Book(isbn='9788498387087',
title='Harry Potter y la Piedra Filosofal',
description='La vida de Harry Potter cambia para siempre el ...'),
Book(isbn='9788498382679',
title='Harry Potter y la camara secreta',
description='Tras derrotar una vez mas a lord Voldemort, ...')]
print(j_rowling.books[1])
print(j_rowling.books[1].title)
session.add(j_rowling)
session.commit()
j_rowling = session.query(Author).filter_by(firstname='Joanne').one()
print(j_rowling.books)
Consultando Objetos Relacionados
Devuelve el autor y libro para este isbn. Los imprimimos en pantalla.
for an_author, a_book in session.query(Author, Book).filter(Author.id==Book.author_id).\
filter(Book.isbn=='9788498387087').\
all():
print(an_author)
print(a_book)
Devuelve el autor del libro con este isbn
print(session.query(Author).join(Book).filter(Book.isbn=='9788498387087').all())
Devuelve los autores de los libros poniendo la condición explícitamente.
print(session.query(Author).join(Book, Author.id==Book.author_id).all())
Devuelve los autores de los libros especificando la relación de izquierda a derecha.
print(session.query(Author).join(Author.books).all())
Devuelve los autores de los libros para una relación específica.
print(session.query(Author).join(Book, Author.books).all())
Devuelve los autores de loslibros utilizando un string.
print(session.query(Author).join('books').all())
Devuelve el nombre del autor del libro utilizando un filtro exists. Es decir,
si existe algún libro del autor.
print(session.query(Author).join('books').all())
stmt = exists().where(Book.author_id == Author.id)
for name,in session.query(Author.firstname).filter(stmt):
print(name)
Devuelve el nombre del autor del libro utilizando un filtro any. Es decir,
si el autor tiene algún libro.
print(session.query(Author).join('books').all())
for name, in session.query(Author.firstname).filter(Author.books.any()):
print(name)
Devuelve el nombre del autor del libro utilizando un filtro like, parecido a any solo
que se le puede definir una condición, en este caso que el apellido del autor contenga
la subcadena de texto 'Row'.
for name, in session.query(Author.firstname).filter(Author.books.any(Author.lastname.like('%Row%'))):
print(name)
Devuelve los libros donde el autor no se llame Joanne.
print(session.query(Book).filter(~Book.author.has(Author.firstname=='Joanne')).all())
Borrando Objetos
Al borrar un autor el/los libros no se borran. Quedando sin autor, objetos huefanos
session.delete(j_rowling)
print(session.query(Author).filter_by(firstname='Joanne').count())
print(session.query(Book).\
filter(Book.isbn.in_(['9788498387087','9788498382679'])).\
count())
session.rollback()
Para borrar el objeto padre y los objetos hijos, definir el atributo cascade con
la opción “all, delete, delete-orphan”
books = relationship("Book", order_by="Book.id", back_populates="author",
cascade="all, delete, delete-orphan")
del j_rowling.books[1]
print(session.query(Book).filter(Book.isbn.in_(['9788498387087',
'9788498382679'])).count())
session.delete(j_rowling)
print(session.query(Author).filter_by(firstname='Joanne').count())
print(session.query(Book).filter(Book.isbn.in_(['9788498387087',
'9788498382679'])).count())
Relaciones entre objetos
Crear relaciones entre modelos
Ej. se muestra una relación uno a muchos (un autor muchos libros):
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Sequence, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship
Base = declarative_base()
engine = create_engine('sqlite:///:memory:')
class Author(Base):
__tablename__ = 'author'
id = Column(Integer, Sequence('author_id_seq'), primary_key=True)
firstname = Column(String)
lastname = Column(String)
"""
Se crea una relación con el modelo Book, ordenado por book.id y
con referencia hacia atrás "author".
"""
# books = relationship("Book", order_by="Book.id", back_populates="author")
# CASCADE
books = relationship("Book",
order_by="Book.id",
back_populates="author",
cascade="all, delete, delete-orphan")
def __repr__(self):
return "{} {}".format(self.firstname, self.lastname)
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, Sequence('book_id_seq'), primary_key=True)
isbn = Column(String)
title = Column(String)
description = Column(String)
"""
Se crea la columna de tipo entero, ForeignKey de author.id
"""
author_id = Column(Integer, ForeignKey('author.id'))
"""
Se crea la relación con el modelo Author, con una
referencia hacia atrás a "books".
"""
author = relationship("Author", back_populates="books")
def __repr__(self):
return "{}".format(self.title)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
j_rowling = Author(firstname='Joanne', lastname='Rowling')
print(j_rowling.books)
j_rowling.books = [Book(isbn='9788498387087',
title='Harry Potter y la Piedra Filosofal',
description='La vida de Harry Potter cambia para siempre el ...'),
Book(isbn='9788498382679',
title='Harry Potter y la camara secreta',
description='Tras derrotar una vez mas a lord Voldemort, ...')]
print(j_rowling.books[1])
print(j_rowling.books[1].title)
session.add(j_rowling)
session.commit()
j_rowling = session.query(Author).filter_by(firstname='Joanne').one()
print(j_rowling.books)
Querys 2
Devuelve Autor y Libro para el isbn especificado
for an_author, a_book in session.query(Author, Book).\
filter(Author.id == Book.author_id).\
filter(Book.isbn == '9788498387087').\
all():
print(an_author, '\n', a_book)
Query #2: Devuelve el Autor del libro con isbn 9788498387087.
session.query(Author).join(Book).filter(Book.isbn == '9788498387087').all())
Query #3: Devuelve los autores de los libros condición explicita.
session.query(Author).join(Book, Author.id == Book.author_id).all())
Query #4: Devuelve los autores y los libros, especificando la relación de izq. a derecha.
session.query(Author).join(Author.books).all())
Query #5: Devuelve los Autores de los libros para una relación específica.
session.query(Author).join(Book, Author.books).all())
Query #6: Busqueda de todos los Autores, usando un string.
session.query(Author).join('books').all())
Query #7: Filtrado por autores con nombre 'Joél Ez'").
stmt = exists().where(Book.author_id == Author.id)
for name, in session.query(Author.firstname).filter(stmt):
print(name)
stmt = exists().where(Book.author_id == Author.id)
>>> NameError: name 'exists' is not defined
Query #8: Devuelve el nombre del autor del libro usando un filtro any.
for name, in session.query(Author.firstname).filter(Author.books.any()):
print(name)
Query #9: Devuelve el nombre del autor, usando un filtro like, se puede
definir una condición. Por ejm. que el apellido contenga la cadena 'Row'.
for name, in session.query(Author.firstname).\
filter(Author.books.any(Author.lastname.like('%Row%'))):
print(name)
Query #10: Devuelve los libros donde el autor no se llame 'Joanne'.
session.query(Book).filter(~Book.author.has(Author.firstname == 'Joanne')).all(),
Borrar objetos de la BD
session.delete(j_rowling)
print("Autores con nombre Joanne :",
session.query(Author).filter_by(firstname='Joanne').count())
print("Libros según isbn 9788498387087 y 9788498382679 :",
session.query(Book).filter(Book.isbn.in_(['9788498387087', '9788498382679'])).count())
Relación muchos a muchos
Agregar una tabla para asociar objetos de dos modelos. La tabla de asociación se indica
en el argumento secondary de la relación.
from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id')))
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child", secondary=association_table,
back_populates="parents")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
parents = relationship("Parent", secondary=association_table,
back_populates="children")
# -*- coding: utf-8 -*-
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, \
Sequence, ForeignKey, Table, Text
from sqlalchemy.orm import sessionmaker, relationship
Base = declarative_base()
# association table
book_categories = Table('book_categories',
Base.metadata,
Column('book_id', ForeignKey('book.id'), primary_key=True),
Column('category_id', ForeignKey('book_category.id'), primary_key=True))
class Author(Base):
__tablename__ = 'author'
id = Column(Integer, Sequence('author_id_seq'), primary_key=True)
firstname = Column(String)
lastname = Column(String)
books = relationship("Book", order_by="Book.id", back_populates="author")
def __repr__(self):
return "{} {}".format(self.firstname, self.lastname)
class BookCategory(Base):
__tablename__ = 'book_category'
id = Column(Integer, Sequence('book_category_id_seq'), primary_key=True)
name = Column(String)
books = relationship('Book', secondary=book_categories,
back_populates='categories')
def __repr__(self):
return "{}".format(self.name)
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, Sequence('book_id_seq'), primary_key=True)
isbn = Column(String)
title = Column(String)
description = Column(String)
author_id = Column(Integer, ForeignKey('author.id'))
author = relationship("Author", back_populates="books")
categories = relationship('BookCategory', secondary=book_categories,
back_populates='books')
def __repr__(self):
return "{}".format(self.title)
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
j_rowling = Author(firstname='Joanne', lastname='Rowling')
session.add(j_rowling)
j_rowling = session.query(Author).filter_by(firstname='Joanne').one()
book = Book(isbn='9788498387087',
title='Harry Potter y la Piedra Filosofal',
description='La vida de Harry Potter cambia para siempre el ...')
book.categories.append(BookCategory(name='Aventura'))
book.categories.append(BookCategory(name='Accion'))
book.author = j_rowling
print(
session.query(Book).filter(Book.categories.any(name='Aventura')).all()
, '\n',
session.query(Book).filter(Book.author == j_rowling).\
filter(Book.categories.any(name='Aventura')).all()
)
# [Harry Potter y la Piedra Filosofal]
# [Harry Potter y la Piedra Filosofal]
Actividad sistema escuela
Realizar un sistema para una escuela.
-
Este sistema permite registrar nuevos alumnos, profesores y cursos.
-
Un alumno es asignado a un curso y un curso puede tener asociado más de un profesor.
-
Los profesores tienen un horario que indica cuando están en cada curso.
-
El horario asociará un curso y un profesor para un día de la semana (Lunes, Martes,
Miércoles, Jueves, Viernes, Sábado, Domingo), una hora desde, y una hora hasta. -
El sistema permitirá exportar los alumnos que pertenecen a un curso, el horario de
cada profesor y el horario del curso.
import csv
import datetime
from sqlalchemy import create_engine, ForeignKey, Column, \
Integer, String, Time, Sequence
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
Base = declarative_base()
class Alumno(Base):
__tablename__ = 'alumno'
id = Column(Integer, primary_key=True)
nombre = Column(String, nullable=False)
curso_id = Column(Integer, ForeignKey('curso.id'))
curso = relationship('Curso', back_populates='inscrito')
def __repr__(self):
return self.nombre
class Profesor(Base):
__tablename__ = 'profesor'
id = Column(Integer, primary_key=True)
nombre = Column(String, nullable=False)
horario_prof = relationship('Horario', order_by='Horario.hr_ini', back_populates='profesor')
def __repr__(self):
return self.nombre
class Curso(Base):
__tablename__ = 'curso'
id = Column(Integer, primary_key=True)
nombre = Column(String, nullable=False)
inscrito = relationship('Alumno', order_by='Alumno.id', back_populates='curso')
horario_curso = relationship('Horario', order_by='Horario.hr_ini', back_populates='curso')
def __repr__(self):
return self.nombre
class Horario(Base):
__tablename__ = 'horario'
id = Column(Integer, Sequence('horario_id_seq'), primary_key=True)
dia = Column(Integer)
hr_ini = Column(Time)
hr_fin = Column(Time)
curso_id = Column(Integer, ForeignKey('curso.id'))
profesor_id = Column(Integer, ForeignKey('profesor.id'))
curso = relationship('Curso', back_populates='horario_curso')
profesor = relationship('Profesor', back_populates='horario_prof')
class ExportarCSV(object):
def __init__(self, ruta):
self.ruta = ruta
def exprt_horario_prof(self, profesor):
horario = profesor.horario_prof
with open(self.ruta, 'w') as archivo:
writer = csv.writer(archivo)
for horas in horario:
writer.writerow([horas.dia, horas.hr_ini, horas.hr_fin, horas.curso.nombre])
def export_hr_curso(self, curso):
horario = curso.horario_curso
with open(self.ruta, 'w') as archivo:
writer = csv.writer(archivo)
for horas in horario:
writer.writerow([horas.dia, horas.hr_ini, horas.hr_fin, horas.profesor])
def export_alumnos_curso(self, curso):
inscritos = curso.inscrito
with open(self.ruta, 'w') as archivo:
writer = csv.writer(archivo)
for alumno in inscritos:
writer.writerow([str(alumno)])
def main(*args, **kwargs):
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
# Creación de profesores
prof1 = Profesor(nombre='Profesor_1')
prof2 = Profesor(nombre='Profesor_2')
prof3 = Profesor(nombre='Profesor_3')
session.add(prof1)
session.add(prof2)
session.add(prof3)
# Creación de Cursos
curso1 = Curso(nombre='Curso_1')
curso2 = Curso(nombre='Curso_2')
curso3 = Curso(nombre='Curso_3')
session.add(curso1)
session.add(curso2)
session.add(curso3)
# Creación de Alumnos
alumno1 = Alumno(nombre='Alumno_1', curso=curso1)
alumno2 = Alumno(nombre='Alumno_2', curso=curso2)
alumno3 = Alumno(nombre='Alumno_3', curso=curso3)
session.add(alumno1)
session.add(alumno2)
session.add(alumno3)
# Creación de objetos Hora
hora1 = datetime.time(9, 0, 0)
hora2 = datetime.time(12, 0, 0)
hora3 = datetime.time(13, 0, 0)
hora4 = datetime.time(15, 0, 0)
# Creación de horas para iniciio y termino de clases
horario1 = Horario(dia=2, hr_ini=hora1, hr_fin=hora2, curso=curso1, profesor=prof1)
horario2 = Horario(dia=3, hr_ini=hora2, hr_fin=hora3, curso=curso2, profesor=prof2)
horario3 = Horario(dia=4, hr_ini=hora2, hr_fin=hora3, curso=curso3, profesor=prof3)
horario4 = Horario(dia=5, hr_ini=hora3, hr_fin=hora4, curso=curso1, profesor=prof3)
session.add(horario1)
session.add(horario2)
session.add(horario3)
session.add(horario4)
session.commit()
# Exportar Datos
ExportarCSV('Horario_Docente_{}'.format(prof1)).exprt_horario_prof(prof1)
ExportarCSV('Horario_Docente_{}'.format(prof2)).exprt_horario_prof(prof2)
ExportarCSV('Horario_Docente_{}'.format(prof3)).exprt_horario_prof(prof3)
ExportarCSV('Horario_Curso_{}'.format(curso1)).export_hr_curso(curso1)
ExportarCSV('Horario_Curso_{}'.format(curso2)).export_hr_curso(curso2)
ExportarCSV('Horario_Curso_{}'.format(curso3)).export_hr_curso(curso3)
ExportarCSV('Alumnos_Inscritos_{}'.format(curso1)).export_alumnos_curso(curso1)
ExportarCSV('Alumnos_Inscritos_{}'.format(curso2)).export_alumnos_curso(curso2)
ExportarCSV('Alumnos_Inscritos_{}'.format(curso3)).export_alumnos_curso(curso3)
if __name__ == "__main__":
main()
──────────────────────────────────
File: Horario_Docente_Profesor_1
──────────────────────────────────
2,09:00:00,12:00:00,Curso_1
──────────────────────────────────
──────────────────────────────────
File: Horario_Docente_Profesor_2
──────────────────────────────────
3,12:00:00,13:00:00,Curso_2
──────────────────────────────────
──────────────────────────────────
File: Horario_Docente_Profesor_3
──────────────────────────────────
4,12:00:00,13:00:00,Curso_3
5,13:00:00,15:00:00,Curso_1
──────────────────────────────────
──────────────────────────────────
File: Horario_Curso_Curso_1
──────────────────────────────────
2,09:00:00,12:00:00,Profesor_1
5,13:00:00,15:00:00,Profesor_3
──────────────────────────────────
File: Horario_Curso_Curso_2
──────────────────────────────────
3,12:00:00,13:00:00,Profesor_2
──────────────────────────────────
──────────────────────────────────
File: Horario_Curso_Curso_3
──────────────────────────────────
4,12:00:00,13:00:00,Profesor_3
──────────────────────────────────
──────────────────────────────────
File: Alumnos_Inscritos_Curso_1
──────────────────────────────────
Alumno_1
──────────────────────────────────
──────────────────────────────────
File: Alumnos_Inscritos_Curso_2
──────────────────────────────────
Alumno_2
──────────────────────────────────
──────────────────────────────────
File: Alumnos_Inscritos_Curso_3
──────────────────────────────────
Alumno_3
──────────────────────────────────