[英]Filtering a relationship attribute in SQLAlchemy
我有一些带有Widget
对象的代码,必须定期进行一些处理。 小部件与Process
对象有关系,该对象跟踪单个处理尝试并保存有关这些尝试的数据,例如状态信息、开始和结束时间以及结果。 这种关系看起来像这样:
class Widget(Base):
_tablename_ = 'widget'
id = Column(Integer, primary_key=True)
name = Column(String)
attempts = relationship('Process')
class Process(Base):
_tablename_ = 'process'
id = Column(Integer, primary_key=True)
widget_id = Column(Integer, ForeignKey='widget.id'))
start = Column(DateTime)
end = Column(DateTime)
success = Column(Boolean)
我想在Widget
上有一个方法来检查是否是时候处理该小部件了。 它需要查看所有尝试,找到最近成功的一次,并查看它是否比阈值更旧。
一种选择是使用列表Widget.attempts
迭代Widget.attempts
。 假设now
和delay
是合理的datetime
和timedelta
对象,那么当定义为Widget
上的方法时,类似的东西会起作用:
def ready(self):
recent_success = [attempt for attempt in self.attempts if attempt.success is True and attempt.end >= now - delay]
if recent_success:
return False
return True
这似乎是很好的惯用 Python,但它没有充分利用支持数据的 SQL 数据库的强大功能,而且它的效率可能低于运行类似的 SQL 查询,尤其是在attempts
列表中有大量 Process 对象时。 不过,我很难找出将其实现为查询的最佳方法。
在Widget
运行查询很容易,如下所示:
def ready(self):
recent_success = session.query(Process).filter(
and_(
Process.widget_id == self.id,
Process.success == True,
Process.end >= now - delay
)
).order_by(Process.end.desc()).first()
if recent_success:
return False
return True
但是我在单元测试中遇到了在定义 Widget 的模块中正确设置session
问题。 在我看来,这是一个糟糕的风格选择,而且可能不是 SQLAlchemy 对象的结构方式。
我可以使ready()
函数成为 Widget 类的外部函数,这将解决在单元测试中设置session
的问题,但这似乎是糟糕的 OO 结构。
我认为理想的情况是,如果我能以某种方式使用比列表理解更有效的类似 SQL 的代码过滤Widget.attempts
,但我没有发现任何表明这是可能的。
对于这样的事情,实际上最好的方法是什么?
您正在朝着正确的方向思考。 Widget
实例中的任何解决方案都意味着您需要处理所有实例。 寻求外部过程将具有更好的性能和更容易的可测试性。
您可以使用此查询获取所有需要安排用于下一次处理的Widget
实例:
q = (
session
.query(Widget)
.filter(Widget.attempts.any(and_(
Process.success == True,
Process.end >= now - delay,
)))
)
widgets_to_process = q.all()
如果你真的想在模型上有一个属性,我不会创建一个单独的查询,而只是使用关系:
def ready(self, at_time):
successes = [
attempt
for attempt in sorted(self.attempts, key=lambda v: v.end)
if attempt.success and attempt.end >= at_time # at_time = now - delay
]
return bool(successes)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.