2187 lines
62 KiB
Markdown
2187 lines
62 KiB
Markdown
**Ir a:**
|
|
[*Repositorio*](https://gitea.kickto.net/devfzn/Apuntes_Python),
|
|
[*Modulo 3*](https://gitea.kickto.net/devfzn/Apuntes_Python/src/branch/master/01_curso/Modulo_3#modulo-3-python-basico)
|
|
|
|
|
|
## Modulo 4 - Python basico
|
|
# Bases de Datos
|
|
|
|
#### [Indice](#modulo-4-python-basico)
|
|
- [Bases de Datos](#bases-de-datos)
|
|
- [Esquemas y datos](#esquemas-y-datos)
|
|
- [Funcionalidad básica SQLiteBrowser](#funcionalidad-básica-sqlitebrowser)
|
|
- [El modelo Relacional](#el-modelo-relacional)
|
|
- [Integridad de Datos](#integridad-de-datos)
|
|
- [Especificación SQL-2003](#especificación-sql-año-2003)
|
|
- [Indices](#indices)
|
|
- [Structured Query Languaje](#structured-query-language)
|
|
- [Sentencias SQL](#sentencias-sql)
|
|
- [SQL Joins](#join)
|
|
- [SQLite - Manejo de BDs en Python](#manejo-de-bases-de-datos-en-python)
|
|
- [Conexion a base de datos](#conexiones-a-base-de-datos)
|
|
- [Cursores](#cursores)
|
|
- [INSERT](#guardar-objeto-en-base-de-datos)
|
|
- [SELECT](#consultar-objetos)
|
|
- [UPDATE](#actualizar-objetos)
|
|
- [DELETE](#borrar-objetos)
|
|
- [ORM y SQLAlchemy](#orm)
|
|
- [Mapeo de los modelos](#mapeo-de-los-modelos)
|
|
- [Guardar Objetos](#guardar-objetos)
|
|
- [Querys](#querys-sqlalchemy)
|
|
- [Construyendo relación entre objetos](#construyendo-relación-entre-objetos)
|
|
- [Consultando objetos relacionados](#consultando-objetos-relacionados)
|
|
- [Borrando objetos](#borrando-objetos)
|
|
- [Relaciones entre objetos](#relaciones-entre-objetos)
|
|
- [Crear relaciones entre modelos](#crear-relaciones-entre-modelos)
|
|
- [Querys 2](#querys-2)
|
|
- [Relación muchos a muchos](#relación-muchos-a-muchos)
|
|
- [Actividad - Sistema escuela](#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](https://es.wikipedia.org/wiki/%C3%81rbol-B),
|
|
[B+](https://es.wikipedia.org/wiki/%C3%81rbol_B%2B) o
|
|
[B\*](https://es.wikipedia.org/wiki/%C3%81rbol-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)|
|
|
|
|
|
|
```sql
|
|
-- 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.
|
|
```sql
|
|
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`
|
|
|
|
```sql
|
|
-- 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.
|
|
```sql
|
|
--SELECT columna1, columna2, ...
|
|
--FROM nombre_tabla;
|
|
--ej.
|
|
SELECT * FROM libro;
|
|
-- * = ALL
|
|
```
|
|
|
|
`SELECT DISTINCT` solo selecciona los valores diferentes.
|
|
```sql
|
|
--SELECT DISTINCT columna1, columna2, ...
|
|
--FROM nombre_tabla;
|
|
--ej.
|
|
SELECT DISTINCT categoria_id FROM libro;
|
|
```
|
|
|
|
`WHERE`
|
|
```sql
|
|
--SELECT columna1, columna2, ...
|
|
--FROM nombre_tabla
|
|
--WHERE condición;
|
|
--ej.
|
|
SELECT * FROM libro WHERE precio < 100;
|
|
```
|
|
|
|
`AND` , `OR` , `NOT`
|
|
```sql
|
|
--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`
|
|
```sql
|
|
--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`
|
|
```sql
|
|
SELECT * FROM libro LIMIT 1;
|
|
```
|
|
|
|
`INSERT`
|
|
```sql
|
|
--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`
|
|
```sql
|
|
--UPDATE nombre_taba
|
|
--SET comumna1 = valor1, columna2 = valor2, ...
|
|
-- WHERE concición;
|
|
--ej.
|
|
UPDATE categoria_libro SET NOMBRE = 'Acción' WHERE CODIGO = 4;
|
|
```
|
|
|
|
`DELETE`
|
|
```sql
|
|
--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.
|
|
```sql
|
|
--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.
|
|
```sql
|
|
--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.
|
|
```sql
|
|
--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;
|
|
```
|
|
![Sql Joins](https://gitea.kickto.net/devfzn/Apuntes_Python/src/branch/master/01_curso/Modulo_4/4-2d_SQL_Joins.png)
|
|
![Sql Joins](./4-2d_SQL_Joins.png)
|
|
|
|
|
|
----
|
|
|
|
|
|
## 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.
|
|
|
|
```python
|
|
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
|
|
```python
|
|
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
|
|
```sql
|
|
--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
|
|
```python
|
|
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.
|
|
```python
|
|
# 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
|
|
```python
|
|
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
|
|
```python
|
|
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.
|
|
```python
|
|
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*](https://gitea.kickto.net/devfzn/IntroPython/raw/branch/master/01_curso/Modulo_4/4-3d_prueba.db)
|
|
|
|
```python
|
|
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
|
|
```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
|
|
```python
|
|
# 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
|
|
```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
|
|
```python
|
|
# 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
|
|
```python
|
|
# 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
|
|
```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
|
|
```python
|
|
# 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
|
|
```python
|
|
# 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
|
|
```python
|
|
# 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.
|
|
|
|
```python
|
|
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
|
|
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**
|
|
```python
|
|
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**
|
|
```python
|
|
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**
|
|
```python
|
|
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**
|
|
```python
|
|
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.
|
|
```python
|
|
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.
|
|
```python
|
|
session.commit()
|
|
print(author.id, '\n')
|
|
```
|
|
|
|
**ROLLBACK**
|
|
Podemos hacer rollback de todos los cambios no incluidos en un commit.
|
|
```python
|
|
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
|
|
```python
|
|
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.
|
|
```python
|
|
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.
|
|
```python
|
|
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.
|
|
```python
|
|
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.
|
|
```python
|
|
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.
|
|
```python
|
|
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.
|
|
```python
|
|
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'
|
|
```python
|
|
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.
|
|
```python
|
|
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.
|
|
```python
|
|
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'.
|
|
```python
|
|
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)
|
|
```python
|
|
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***.
|
|
```python
|
|
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.
|
|
```python
|
|
|
|
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
|
|
```python
|
|
print(session.query(Author).join(Book).filter(Book.isbn=='9788498387087').all())
|
|
```
|
|
|
|
Devuelve los autores de los libros poniendo la condición explícitamente.
|
|
```python
|
|
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.
|
|
```python
|
|
print(session.query(Author).join(Author.books).all())
|
|
```
|
|
|
|
Devuelve los autores de los libros para una relación específica.
|
|
```python
|
|
print(session.query(Author).join(Book, Author.books).all())
|
|
```
|
|
|
|
Devuelve los autores de loslibros utilizando un string.
|
|
```python
|
|
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.
|
|
```python
|
|
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.
|
|
```python
|
|
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'*.
|
|
```python
|
|
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.
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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**”
|
|
```python
|
|
books = relationship("Book", order_by="Book.id", back_populates="author",
|
|
cascade="all, delete, delete-orphan")
|
|
```
|
|
|
|
```python
|
|
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):
|
|
```python
|
|
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
|
|
```python
|
|
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.
|
|
```python
|
|
session.query(Author).join(Book).filter(Book.isbn == '9788498387087').all())
|
|
```
|
|
|
|
**Query #3**: Devuelve los autores de los libros ***condición explicita***.
|
|
```python
|
|
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.
|
|
```python
|
|
session.query(Author).join(Author.books).all())
|
|
```
|
|
|
|
**Query #5**: Devuelve los Autores de los libros para una relación específica.
|
|
```python
|
|
session.query(Author).join(Book, Author.books).all())
|
|
```
|
|
|
|
**Query #6**: Busqueda de todos los Autores, usando un string.
|
|
```python
|
|
session.query(Author).join('books').all())
|
|
```
|
|
|
|
**Query #7**: Filtrado por autores con nombre 'Joél Ez'").
|
|
```python
|
|
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**.
|
|
```python
|
|
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'*.
|
|
```python
|
|
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'*.
|
|
```python
|
|
session.query(Book).filter(~Book.author.has(Author.firstname == 'Joanne')).all(),
|
|
```
|
|
|
|
|
|
### Borrar objetos de la BD
|
|
|
|
```python
|
|
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.
|
|
```python
|
|
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")
|
|
```
|
|
|
|
```python
|
|
# -*- 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.
|
|
|
|
|
|
```python
|
|
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
|
|
──────────────────────────────────
|
|
```
|
|
|