Вы возвращаете скалярный подзапрос из своего гибридного свойства при использовании в контексте SQL (класса), поэтому просто используйте его, как если бы вы использовали выражение значения:
db.session.query(Worker).\
filter(Worker.latest_job >= datetime.datetime(2017, 5, 10, 0, 2, 45, 932983)).\
count()
Требуется само гибридное свойстводля явной обработки корреляции в этом случае:
@latest_job.expression
def latest_job(cls):
Job = db.Model._decl_class_registry.get('Job')
return select([func.max(Job.started)]).\
where(cls.id == Job.worker_id).\
correlate(cls).\
as_scalar()
Обратите внимание, что существует некоторая асимметрия между стороной Python гибридного свойства и стороной SQL.Он генерирует последний объект Job
при обращении к экземпляру по сравнению с созданием коррелированного скалярного подзапроса max(started)
в SQL.Если вы хотите, чтобы он также возвращал строку Job
в SQL, вы должны сделать что-то вроде
@latest_job.expression
def latest_job(cls):
Job = db.Model._decl_class_registry.get('Job')
return select([Job]).\
where(cls.id == Job.worker_id).\
order_by(Job.started.desc()).\
limit(1).\
correlate(cls).\
subquery()
, но на самом деле это менее полезно, потому что обычно - но не всегда - такого родакоррелированный подзапрос будет медленнее, чем объединение с подзапросом.Например, чтобы получить работников с последними заданиями, которые соответствуют исходным критериям:
job_alias = db.aliased(Job)
# This reads as: find worker_id and started of jobs that have no matching
# jobs with the same worker_id and greater started, or in other words the
# worker_id, started of the latest jobs.
latest_jobs = db.session.query(Job.worker_id, Job.started).\
outerjoin(job_alias, and_(Job.worker_id == job_alias.worker_id,
Job.started < job_alias.started)).\
filter(job_alias.id == None).\
subquery()
db.session.query(Worker).\
join(latest_jobs, Worker.id == latest_jobs.c.worker_id).\
filter(latest_jobs.c.started >= datetime.datetime(2017, 5, 10, 0, 2, 45, 932983)).\
count()
и, конечно, если вы просто хотите подсчитать, то объединение вообще не нужно:
job_alias = db.aliased(Job)
db.session.query(func.count()).\
outerjoin(job_alias, and_(Job.worker_id == job_alias.worker_id,
Job.started < job_alias.started)).\
filter(job_alias.id == None,
Job.started >= datetime.datetime(2017, 5, 10, 0, 2, 45, 932983)).\
scalar()
Обратите внимание, что вызов Query.scalar()
не совпадает с Query.as_scalar()
, но просто возвращает первое значение первой строки.