Relaciones de muchos a muchos

Many to Many relationshipentre dos tablas se logra agregando una tabla de asociación de modo que tenga dos claves externas, una de la clave principal de cada tabla. Además, las clases que se asignan a las dos tablas tienen un atributo con una colección de objetos de otras tablas de asociación asignados como atributo secundario de la función de relación ().

Para ello, crearemos una base de datos SQLite (mycollege.db) con dos tablas: departamento y empleado. Aquí, asumimos que un empleado es parte de más de un departamento y un departamento tiene más de un empleado. Esto constituye una relación de muchos a muchos.

La definición de las clases de Empleado y Departamento asignadas a la tabla de departamento y empleado es la siguiente:

from sqlalchemy import create_engine, ForeignKey, Column, Integer, String
engine = create_engine('sqlite:///mycollege.db', echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy.orm import relationship

class Department(Base):
   __tablename__ = 'department'
   id = Column(Integer, primary_key = True)
   name = Column(String)
   employees = relationship('Employee', secondary = 'link')
   
class Employee(Base):
   __tablename__ = 'employee'
   id = Column(Integer, primary_key = True)
   name = Column(String)
   departments = relationship(Department,secondary='link')

Ahora definimos una clase Link. Está vinculado a la tabla de vínculos y contiene los atributos department_id y employee_id, respectivamente, que hacen referencia a las claves principales de la tabla de departamento y empleado.

class Link(Base):
   __tablename__ = 'link'
   department_id = Column(
      Integer, 
      ForeignKey('department.id'), 
      primary_key = True)

employee_id = Column(
   Integer, 
   ForeignKey('employee.id'), 
   primary_key = True)

Aquí, tenemos que tomar nota de que la clase Departamento tiene un atributo de empleados relacionado con la clase Empleado. Al atributo secundario de la función de relación se le asigna un vínculo como su valor.

De manera similar, la clase Empleado tiene un atributo de departamentos relacionado con la clase Departamento. Al atributo secundario de la función de relación se le asigna un vínculo como su valor.

Todas estas tres tablas se crean cuando se ejecuta la siguiente declaración:

Base.metadata.create_all(engine)

La consola de Python emite las siguientes consultas CREATE TABLE:

CREATE TABLE department (
   id INTEGER NOT NULL,
   name VARCHAR,
   PRIMARY KEY (id)
)

CREATE TABLE employee (
   id INTEGER NOT NULL,
   name VARCHAR,
   PRIMARY KEY (id)
)

CREATE TABLE link (
   department_id INTEGER NOT NULL,
   employee_id INTEGER NOT NULL,
   PRIMARY KEY (department_id, employee_id),
   FOREIGN KEY(department_id) REFERENCES department (id),
   FOREIGN KEY(employee_id) REFERENCES employee (id)
)

Podemos verificar esto abriendo mycollege.db usando SQLiteStudio como se muestra en las capturas de pantalla que se muestran a continuación:

A continuación, creamos tres objetos de la clase Departamento y tres objetos de la clase Empleado como se muestra a continuación:

d1 = Department(name = "Accounts")
d2 = Department(name = "Sales")
d3 = Department(name = "Marketing")

e1 = Employee(name = "John")
e2 = Employee(name = "Tony")
e3 = Employee(name = "Graham")

Cada tabla tiene un atributo de colección que tiene el método append (). Podemos agregar objetos Empleados a la colección Empleados del objeto Departamento. De manera similar, podemos agregar objetos Departamento al atributo de colección de departamentos de los objetos Empleado.

e1.departments.append(d1)
e2.departments.append(d3)
d1.employees.append(e3)
d2.employees.append(e2)
d3.employees.append(e1)
e3.departments.append(d2)

Todo lo que tenemos que hacer ahora es configurar un objeto de sesión, agregarle todos los objetos y confirmar los cambios como se muestra a continuación:

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind = engine)
session = Session()
session.add(e1)
session.add(e2)
session.add(d1)
session.add(d2)
session.add(d3)
session.add(e3)
session.commit()

Las siguientes declaraciones SQL se emitirán en la consola de Python:

INSERT INTO department (name) VALUES (?) ('Accounts',)
INSERT INTO department (name) VALUES (?) ('Sales',)
INSERT INTO department (name) VALUES (?) ('Marketing',)
INSERT INTO employee (name) VALUES (?) ('John',)
INSERT INTO employee (name) VALUES (?) ('Graham',)
INSERT INTO employee (name) VALUES (?) ('Tony',)
INSERT INTO link (department_id, employee_id) VALUES (?, ?) ((1, 2), (3, 1), (2, 3))
INSERT INTO link (department_id, employee_id) VALUES (?, ?) ((1, 1), (2, 2), (3, 3))

Para verificar el efecto de las operaciones anteriores, use SQLiteStudio y vea los datos en las tablas de departamentos, empleados y enlaces -

Para mostrar los datos, ejecute la siguiente declaración de consulta:

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind = engine)
session = Session()

for x in session.query( Department, Employee).filter(Link.department_id == Department.id, 
   Link.employee_id == Employee.id).order_by(Link.department_id).all():
   print ("Department: {} Name: {}".format(x.Department.name, x.Employee.name))

Según los datos rellenados en nuestro ejemplo, la salida se mostrará como se muestra a continuación:

Department: Accounts Name: John
Department: Accounts Name: Graham
Department: Sales Name: Graham
Department: Sales Name: Tony
Department: Marketing Name: John
Department: Marketing Name: Tony