如何将 sqlalchemy 查询过滤到所有 Parents w/o Children 和所有 Parents 符合 Flask Form 条件的人
How to filter an sqlalchemy query to all Parents w/o Children, and all Parents, who fall under conditions in a Flask Form
首先,我对编码很陌生,如果不值得关注,请提前致歉。
我处理一对多关系。假设我有一个 Parent class 和一个 Child class 定义如下:
class Parent(db.Model):
__tablename__ = 'parent'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128), nullable = False)
age = db.Column(db.Integer(32), nullable = False)
children = db.relationship('Child', backref='parent', lazy='dynamic')
class Child(db.Model):
__tablename__ = 'child'
id = db.Column(db.Integer, primary_key=True)
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id'), nullable = False)
weight = db.Column(db.Integer(32), nullable = False)
我想做的是外连接表并显示所有 parent 的信息(姓名、年龄):
- 要么没有child任
- 或者 parents 和 children 都满足先前通过 Flask 表单输入的条件(用户提交一个范围(假设 child 的最小和最大重量以及相同年龄))。 Parent 如果他们至少有一个 child 满足条件并且 parent 本身满足它。
我成功获得了满足 1 或 2 的查询。这些查询:
#1 有效!
parentssql=session.query(Parent, Child)\
.outerjoin(Child)\
.filter(Child.id == None)\
.distinct(Parent.name)\
.group_by(Parent.name)\
.order_by(Parent.name)\
.all()
#2 也有效!
parents=session.query(Parent, Child)\
.outerjoin(Child)\
.filter(Parent.age.between(form.age_min.data, form.age_max.data), Child.weight.between(form.weight_min.data, form.weight_max.data))\
.distinct(Parent.name)\
.group_by(Parent.name)\
.order_by(Parent.name)\
.all()
那么如何在不进行太多查询的情况下将它们组合起来(基本上尽可能高效(问号))
谢谢!
编辑 1:
我尝试使用 or
和 and
条件,但它给了我一个错误。首先我将添加我编辑的代码(最初是关于星系和线检测)
编辑后的查询:
galaxies=session.query(Galaxy, Line)\
.outerjoin(Line)\
.filter(Galaxy.name.contains(form_advanced.name.data) \
& (Galaxy.right_ascension.between(form_advanced.right_ascension_min.data, form_advanced.right_ascension_max.data) | Galaxy.right_ascension == None ) \
& (Galaxy.declination.between(form_advanced.declination_min.data, form_advanced.declination_max.data) | Galaxy.declination == None ) \
& (Galaxy.redshift.between(form_advanced.redshift_min.data, form_advanced.redshift_max.data) | Galaxy.redshift == None ) \
& (Galaxy.lensing_flag.contains(form_advanced.lensing_flag.data) | Galaxy.lensing_flag == None))
这是我添加条件的地方 (Child.id == None) | (条件 1 & 条件 2 & ...)
galaxies = galaxies.filter((Line.id == None) | ((Line.j_upper.between(form_advanced.j_upper_min.data, form_advanced.j_upper_max.data) | Line.j_upper == None ) \
& (Line.line_id_type.contains(form_advanced.line_id_type.data) | Line.line_id_type == None) \
& (Line.integrated_line_flux.between(form_advanced.integrated_line_flux_min.data, form_advanced.integrated_line_flux_max.data) | Line.integrated_line_flux == None) \
& (Line.peak_line_flux.between(form_advanced.peak_line_flux_min.data, form_advanced.peak_line_flux_max.data) | Line.peak_line_flux == None) \
& (Line.line_width.between(form_advanced.line_width_min.data, form_advanced.line_width_max.data) | Line.line_width == None ) \
& (Line.observed_line_frequency.between(form_advanced.observed_line_frequency_min.data, form_advanced.observed_line_frequency_max.data) | Line.observed_line_frequency == None ) \
& (Line.detection_type.contains(form_advanced.detection_type.data) | Line.detection_type == None) \
& (Line.observed_beam_major.between(form_advanced.observed_beam_major_min.data, form_advanced.observed_beam_major_max.data) | Line.observed_beam_major == None ) \
& (Line.observed_beam_minor.between(form_advanced.observed_beam_minor_min.data, form_advanced.observed_beam_minor_max.data) | Line.observed_beam_minor == None ) \
& (Line.reference.contains(form_advanced.reference.data), Line.reference == None) ))
galaxies = galaxies.distinct(Galaxy.name).group_by(Galaxy.name).order_by(Galaxy.name).all()
这就是我遇到的错误:
sqlalchemy.exc.ArgumentError: SQL expression for WHERE/HAVING role expected, got (<sqlalchemy.sql.elements.BinaryExpression object at 0x7f381585b790>, <sqlalchemy.sql.elements.BinaryExpression object at 0x7f3814eb6670>).
编辑 2
解决了问题并使其与下面@vitaliy 的评论一起使用!
尝试Query.union
。
示例:文档中的逐字记录:
q1 = sess.query(SomeClass).filter(SomeClass.foo=='bar')
q2 = sess.query(SomeClass).filter(SomeClass.bar=='foo')
q3 = q1.union(q2)
在您的情况下,您应该在执行 union
之前删除 .all()
。
我也会考虑对结果查询只做一次 .order_by(..)
。
我还认为不需要 .distinct(Parent.name)
因为你已经做了 .group_by(Parent.name)
这将删除重复项。
您可以向 filter
函数添加更多条件,然后使用 or_
和 and_
:
parents = session.query(Parent, Child)\
.outerjoin(Child)\
.filter(or_(and_(Parent.age.between(age_min, age_max), Child.weight.between(weight_min, weight_max)), Child.id == None))\
.distinct(Parent.name)\
.group_by(Parent.name)\
.order_by(Parent.name)\
.all()
我不确定您的代码中 or_
背后的意图是什么,但现在它什么也没做,因为只有一个参数传递给 or_
函数。
如果您的意思是 Child.weight 或 Parent.age 应该满足提供的条件,那么您的过滤器应该如下所示:
filter(or_(Parent.age.between(age_min, age_max) Child.weight.between(weight_min, weight_max), Child.id == None))
我还建议让您的代码少一些冗余,多一些 Pythonic:
parents = session.query(Parent).outerjoin(Child)\
.filter((Parent.age.between(age_min, age_max) & Child.weight.between(weight_min, weight_max)) | (Child.id == None))\
.order_by(Parent.name)\
.all()
首先,我对编码很陌生,如果不值得关注,请提前致歉。
我处理一对多关系。假设我有一个 Parent class 和一个 Child class 定义如下:
class Parent(db.Model):
__tablename__ = 'parent'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128), nullable = False)
age = db.Column(db.Integer(32), nullable = False)
children = db.relationship('Child', backref='parent', lazy='dynamic')
class Child(db.Model):
__tablename__ = 'child'
id = db.Column(db.Integer, primary_key=True)
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id'), nullable = False)
weight = db.Column(db.Integer(32), nullable = False)
我想做的是外连接表并显示所有 parent 的信息(姓名、年龄):
- 要么没有child任
- 或者 parents 和 children 都满足先前通过 Flask 表单输入的条件(用户提交一个范围(假设 child 的最小和最大重量以及相同年龄))。 Parent 如果他们至少有一个 child 满足条件并且 parent 本身满足它。
我成功获得了满足 1 或 2 的查询。这些查询:
#1 有效!
parentssql=session.query(Parent, Child)\
.outerjoin(Child)\
.filter(Child.id == None)\
.distinct(Parent.name)\
.group_by(Parent.name)\
.order_by(Parent.name)\
.all()
#2 也有效!
parents=session.query(Parent, Child)\
.outerjoin(Child)\
.filter(Parent.age.between(form.age_min.data, form.age_max.data), Child.weight.between(form.weight_min.data, form.weight_max.data))\
.distinct(Parent.name)\
.group_by(Parent.name)\
.order_by(Parent.name)\
.all()
那么如何在不进行太多查询的情况下将它们组合起来(基本上尽可能高效(问号))
谢谢!
编辑 1:
我尝试使用 or
和 and
条件,但它给了我一个错误。首先我将添加我编辑的代码(最初是关于星系和线检测)
编辑后的查询:
galaxies=session.query(Galaxy, Line)\
.outerjoin(Line)\
.filter(Galaxy.name.contains(form_advanced.name.data) \
& (Galaxy.right_ascension.between(form_advanced.right_ascension_min.data, form_advanced.right_ascension_max.data) | Galaxy.right_ascension == None ) \
& (Galaxy.declination.between(form_advanced.declination_min.data, form_advanced.declination_max.data) | Galaxy.declination == None ) \
& (Galaxy.redshift.between(form_advanced.redshift_min.data, form_advanced.redshift_max.data) | Galaxy.redshift == None ) \
& (Galaxy.lensing_flag.contains(form_advanced.lensing_flag.data) | Galaxy.lensing_flag == None))
这是我添加条件的地方 (Child.id == None) | (条件 1 & 条件 2 & ...)
galaxies = galaxies.filter((Line.id == None) | ((Line.j_upper.between(form_advanced.j_upper_min.data, form_advanced.j_upper_max.data) | Line.j_upper == None ) \
& (Line.line_id_type.contains(form_advanced.line_id_type.data) | Line.line_id_type == None) \
& (Line.integrated_line_flux.between(form_advanced.integrated_line_flux_min.data, form_advanced.integrated_line_flux_max.data) | Line.integrated_line_flux == None) \
& (Line.peak_line_flux.between(form_advanced.peak_line_flux_min.data, form_advanced.peak_line_flux_max.data) | Line.peak_line_flux == None) \
& (Line.line_width.between(form_advanced.line_width_min.data, form_advanced.line_width_max.data) | Line.line_width == None ) \
& (Line.observed_line_frequency.between(form_advanced.observed_line_frequency_min.data, form_advanced.observed_line_frequency_max.data) | Line.observed_line_frequency == None ) \
& (Line.detection_type.contains(form_advanced.detection_type.data) | Line.detection_type == None) \
& (Line.observed_beam_major.between(form_advanced.observed_beam_major_min.data, form_advanced.observed_beam_major_max.data) | Line.observed_beam_major == None ) \
& (Line.observed_beam_minor.between(form_advanced.observed_beam_minor_min.data, form_advanced.observed_beam_minor_max.data) | Line.observed_beam_minor == None ) \
& (Line.reference.contains(form_advanced.reference.data), Line.reference == None) ))
galaxies = galaxies.distinct(Galaxy.name).group_by(Galaxy.name).order_by(Galaxy.name).all()
这就是我遇到的错误:
sqlalchemy.exc.ArgumentError: SQL expression for WHERE/HAVING role expected, got (<sqlalchemy.sql.elements.BinaryExpression object at 0x7f381585b790>, <sqlalchemy.sql.elements.BinaryExpression object at 0x7f3814eb6670>).
编辑 2
解决了问题并使其与下面@vitaliy 的评论一起使用!
尝试Query.union
。
示例:文档中的逐字记录:
q1 = sess.query(SomeClass).filter(SomeClass.foo=='bar')
q2 = sess.query(SomeClass).filter(SomeClass.bar=='foo')
q3 = q1.union(q2)
在您的情况下,您应该在执行 union
之前删除 .all()
。
我也会考虑对结果查询只做一次 .order_by(..)
。
我还认为不需要 .distinct(Parent.name)
因为你已经做了 .group_by(Parent.name)
这将删除重复项。
您可以向 filter
函数添加更多条件,然后使用 or_
和 and_
:
parents = session.query(Parent, Child)\
.outerjoin(Child)\
.filter(or_(and_(Parent.age.between(age_min, age_max), Child.weight.between(weight_min, weight_max)), Child.id == None))\
.distinct(Parent.name)\
.group_by(Parent.name)\
.order_by(Parent.name)\
.all()
我不确定您的代码中 or_
背后的意图是什么,但现在它什么也没做,因为只有一个参数传递给 or_
函数。
如果您的意思是 Child.weight 或 Parent.age 应该满足提供的条件,那么您的过滤器应该如下所示:
filter(or_(Parent.age.between(age_min, age_max) Child.weight.between(weight_min, weight_max), Child.id == None))
我还建议让您的代码少一些冗余,多一些 Pythonic:
parents = session.query(Parent).outerjoin(Child)\
.filter((Parent.age.between(age_min, age_max) & Child.weight.between(weight_min, weight_max)) | (Child.id == None))\
.order_by(Parent.name)\
.all()