Поскольку вы уже используете функции ORM, он действительно похож на SQLAlchemy с множественными отношениями "многие ко многим" , и в этом сценарии есть учебник *1003*, однако с небольшим Разница, что вы хотите класс для AssocCompaniesActivities
.
Преамбула для импорта и других настроек, чтобы ваш исходный код работал:
from sqlalchemy import *
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(bind=engine)
Base = declarative_base()
Это обновленное определение класса:
from sqlalchemy.orm import relationship
class Companies(Base):
__tablename__ = 'companies'
identifier = Column(Integer, primary_key=True)
id = Column(String)
label = Column(String)
activities_area = relationship("Activities", secondary='assoc_companies_activities', back_populates='companies')
def __init__(self, id, label):
self.id = id
self.label = label
class Activities(Base):
__tablename__ = 'activities_area'
identifier = Column(Integer, primary_key=True)
name = Column(String)
companies = relationship("Companies", secondary='assoc_companies_activities', back_populates='activities_area')
def __init__(self, name):
self.name = name
class AssocCompaniesActivities(Base):
__tablename__ = 'assoc_companies_activities'
identifier = Column(Integer, primary_key=True)
company_id = Column(Integer, ForeignKey("companies.identifier"), nullable=True)
activities_area_id = Column(Integer, ForeignKey("activities_area.identifier"), nullable=True)
# Should declare primary key for company_id and activities_area_id,
# or better yet, just create a simple un-mapped table like in the docs
def __init__(self, company_id , activities_area_id):
self.company_id = company_id
self.activities_area_id = activities_area_id
Настройка БД, сеанс:
session = Session()
Base.metadata.create_all(engine)
Наконец, добавьте пример данных:
company1 = Companies(id='company1', label='first company')
company2 = Companies(id='company2', label='second company')
banks_activity = Activities('banks')
luxury_activity = Activities('luxury')
paper_activity = Activities('paper')
session.add(company1)
session.add(company2)
session.add(banks_activity)
session.add(luxury_activity)
session.add(paper_activity)
session.commit()
Также отношения (после установления идентификатора компании и деятельности)
company1_luxury = AssocCompaniesActivities(company1.identifier, luxury_activity.identifier)
company2_banks = AssocCompaniesActivities(company2.identifier, banks_activity.identifier)
company2_paper = AssocCompaniesActivities(company2.identifier, paper_activity.identifier)
session.add(company1_luxury)
session.add(company2_banks)
session.add(company2_paper)
session.commit()
После обновления модели мы можем выполнить запрос соединения, используя одну из техник загрузки отношений для быстрой загрузки. Это та часть, в которой она отличается от других вопросов, и что существует не так много примеров, которые объединяют отношения «многие ко многим» с этой конкретной техникой, чтобы создать некоторые для объединенного запроса. Мы можем достичь того, что вы хотите, используя joinedload
:
from sqlalchemy.orm import joinedload
companies = session.query(Companies).options(
joinedload(Companies.activities_area).load_only('name')).all()
, который генерирует этот запрос:
SELECT companies.identifier AS companies_identifier, companies.id AS companies_id, companies.label AS companies_label, activities_area_1.identifier AS activities_area_1_identifier, activities_area_1.name AS activities_area_1_name
FROM companies LEFT OUTER JOIN (assoc_companies_activities AS assoc_companies_activities_1 JOIN activities_area AS activities_area_1 ON activities_area_1.identifier = assoc_companies_activities_1.activities_area_id) ON companies.identifier = assoc_companies_activities_1.company_id
Наконец, превратите результаты в желаемую структуру данных:
print(json.dumps([{
'id': c.id,
'label': c.label,
'activities_area': [a.name for a in c.activities_area]
} for c in companies], indent=4))
Выход:
[
{
"id": "company1",
"label": "first company",
"activities_area": [
"luxury"
]
},
{
"id": "company2",
"label": "second company",
"activities_area": [
"banks",
"paper"
]
}
]