У меня есть следующая установка:
Набор соревнований, проводимых в указанные даты, глобальные участники с датами рождения, которые могут быть зарегистрированы для некоторых из этих соревнований, возрастные классы, определенные для каждого соревнования и, наконец, регистрация конкретного c участника для конкретного c конкурса. Эти регистрации должны наконец содержать правильный возрастной класс. Я мог бы добиться этого, например, hybrid_property, но тогда я столкнулся бы с проблемой N + 1. Моя цель состоит в том, чтобы загружать регистрации с помощью одного большого объединенного запроса, включая возрастные классы.
Вот что я пробовал до сих пор:
from sqlalchemy import create_engine, Column, ForeignKey, Integer, Date, select, func, cast, and_
from sqlalchemy.ext.declarative import declarative_base, declared_attr
from sqlalchemy.orm import scoped_session, sessionmaker, relationship, column_property, joinedload, Load, foreign, remote
import transaction
import inflection
import datetime
import logging
logging.basicConfig()
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
engine = create_engine('sqlite://')
db = scoped_session(sessionmaker())
db.configure(bind=engine)
DBModel = declarative_base()
DBModel.metadata.bind = engine
class Competition(DBModel):
__tablename__ = 'competitions'
id = Column(Integer, primary_key=True)
date = Column(Date, nullable=False)
class Competitor(DBModel):
__tablename__ = 'competitors'
id = Column(Integer, primary_key=True)
birthdate = Column(Date, nullable=False)
class AgeClass(DBModel):
__tablename__ = 'age_classes'
id = Column(Integer, primary_key=True)
competition_id = Column(ForeignKey(Competition.id), nullable=False)
min = Column(Integer, nullable=False)
competition = relationship(Competition)
class Registration(DBModel):
__tablename__ = 'registrations'
id = Column(Integer, primary_key=True)
competition_id = Column(ForeignKey(Competition.id), nullable=False)
competitor_id = Column(ForeignKey(Competitor.id), nullable=False)
competition = relationship(Competition)
competitor = relationship(Competitor)
age = column_property(select([cast((func.julianday(Competition.date) - func.julianday(Competitor.birthdate)) / 365.2425, Integer)]).
where(and_(Competition.id == competition_id,
Competitor.id == competitor_id)).
correlate_except())
age_class_id = column_property(select([AgeClass.id]).
where(and_(AgeClass.competition_id == competition_id,
AgeClass.min <= age)).
order_by(AgeClass.min.desc()).limit(1).
correlate_except(AgeClass))
# age_class = relationship(AgeClass, viewonly=True)
# age_class = relationship(AgeClass, foreign_keys=[age_class_id], viewonly=True)
# age_class = relationship(AgeClass, primaryjoin=AgeClass.id == remote(age_class_id), viewonly=True)
age_class = relationship(AgeClass, primaryjoin=foreign(AgeClass.id) == remote(age_class_id), viewonly=True)
DBModel.metadata.create_all()
with transaction.manager:
competition = Competition(date=datetime.date(2020, 2, 9))
competitor = Competitor(birthdate=datetime.date(2000, 2, 9))
db.add(AgeClass(competition=competition, min=6))
db.add(AgeClass(competition=competition, min=10))
db.add(Registration(competition=competition, competitor=competitor))
query = db.query(Registration).options(joinedload(Registration.competition, innerjoin=True),
joinedload(Registration.competitor, innerjoin=True),
joinedload(Registration.age_class, innerjoin=True),
Load(Registration).raiseload('*'))
print([x.age_class for x in query.first()])
Первые две версии атрибут age_class приводит к сообщению об ошибке
sqlalchemy.ex c .NoForeignKeysError: Не удалось определить условие соединения между родительскими / дочерними таблицами для отношения Registration.age_class - нет внешних ключей, связывающих эти таблицы , Убедитесь, что ссылающиеся столбцы связаны с ForeignKey или ForeignKeyConstraint, или укажите выражение «primaryjoin».
Поэтому я попытался указать условие первичного соединения, которое выдает мне следующее сообщение об ошибке для версий 3 и 4:
sqlalchemy.ex c .ArgumentError: Relationship Registration.age_class не может определить однозначные пары локальных / удаленных столбцов на основе условия соединения и аргументов remote_side. Попробуйте использовать аннотацию remote (), чтобы точно пометить те элементы условия соединения, которые находятся на удаленной стороне отношения.
Есть ли шанс запустить это или я пытаюсь что-то сделать здесь невозможно? Или есть другой способ подойти к этому вопросу?