SQLAlchemy: гибридное выражение с отношением
У меня есть две модели Flask-SQLAlchemy с простым отношением один-ко-многим, как минимальный пример ниже:
class School(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30))
address = db.Column(db.String(30))
class Teacher(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30))
id_school = db.Column(db.Integer, db.ForeignKey(School.id))
school = relationship('School', backref='teachers')
Затем я добавляю гибридное свойство к учителю, которое использует отношения, например:
@hybrid_property
def school_name(self):
return self.school.name
И это свойство прекрасно работает, когда я использую его какteacher_instance.school_name
, Однако я'также хотел бы делать запросы, какTeacher.query.filter(Teacher.school_name == 'x')
, но это дает мне ошибку:
`AttributeError: Neither 'InstrumentedAttribute' object nor
'Comparator' object has an attribute 'school_name'`.
Следуя документации по SQLAlchemy, я добавил простое гибридное выражение, подобное следующему:
@school_name.expression
def school_name(cls):
return School.name
Однако, когда я снова пытаюсь выполнить тот же запрос, он генерирует SQL-запрос без предложения соединения, поэтому я получаю все доступные строки в School, а не только те, которые соответствуют внешнему ключу в Teacher.
Из документации по SQLAlchemy я понял, что выражение ожидает контекст, в котором соединение уже присутствует, поэтому я повторил запрос еще раз:
Teacher.query.join(School).filter(Teacher.school_name == 'x')
И это на самом деле работает, но это побеждает цель попытаться получить синтаксический сахар в первую очередь, если мне нужны знания школьной модели, чтобы получить это. Я ожидаю тамспособ получить это соединение в выражении, но я не могне могу найти это где угодно. В документации есть пример с выражением, возвращающим подзапрос, созданный непосредственно с помощьюselect()
, но даже это неТ работал на меня.
Есть идеи?
ОБНОВИТЬ
После ЕвыОтвет ниже, я использовал прокси-сервер ассоциации, как это было предложено, и он работает, но мне также стало любопытно, что он должен работать сselect()
подзапрос и попытался выяснить, что я сделал не так. Моя первоначальная попытка была:
@school_name.expression
def school_name(cls):
return select(School.name).where(cls.id_school == School.id).as_scalar()
И оказывается, что это выдало мне ошибку, потому что я пропустил список в select (). Код ниже работает нормально:
@school_name.expression
def school_name(cls):
return select([School.name]).where(cls.id_school == School.id).as_scalar()