Relacionamentos muitos para muitos

Many to Many relationshipentre duas tabelas é obtido adicionando uma tabela de associação de forma que ela tenha duas chaves estrangeiras - uma de cada chave primária da tabela. Além disso, o mapeamento de classes para as duas tabelas tem um atributo com uma coleção de objetos de outras tabelas de associação atribuídos como atributo secundário da função relacionamento ().

Para isso, criaremos um banco de dados SQLite (mycollege.db) com duas tabelas - departamento e funcionário. Aqui, presumimos que um funcionário faça parte de mais de um departamento e um departamento tenha mais de um funcionário. Isso constitui um relacionamento de muitos para muitos.

A definição das classes de funcionários e departamentos mapeadas para o departamento e a tabela de funcionários é a seguinte -

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')

Agora definimos uma classe Link. Ele está vinculado à tabela de links e contém os atributos department_id e employee_id, respectivamente, fazendo referência às chaves primárias da tabela de departamento e funcionário.

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)

Aqui, temos que observar que a classe Department tem atributos de funcionários relacionados à classe Employee. O atributo secundário da função de relacionamento recebe um link como seu valor.

Da mesma forma, a classe Employee tem atributos de departamentos relacionados à classe Department. O atributo secundário da função de relacionamento recebe um link como seu valor.

Todas essas três tabelas são criadas quando a seguinte instrução é executada -

Base.metadata.create_all(engine)

O console Python emite as seguintes 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 isso abrindo mycollege.db usando SQLiteStudio, conforme mostrado nas capturas de tela fornecidas abaixo -

Em seguida, criamos três objetos da classe Department e três objetos da classe Employee, conforme mostrado abaixo -

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 tabela possui um atributo de coleção com o método append (). Podemos adicionar objetos Employee à coleção Employees do objeto Department. Da mesma forma, podemos adicionar objetos Department aos atributos de coleção departamentos dos objetos Employee.

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

Tudo o que temos que fazer agora é configurar um objeto de sessão, adicionar todos os objetos a ele e confirmar as alterações conforme mostrado abaixo -

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()

As seguintes instruções SQL serão emitidas no console 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 o efeito das operações acima, use SQLiteStudio e visualize os dados nas tabelas de departamento, funcionário e link -

Para exibir os dados, execute a seguinte instrução 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))

De acordo com os dados preenchidos em nosso exemplo, a saída será exibida conforme abaixo -

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