简体   繁体   English

如何将 sqlalchemy 查询过滤到所有符合 Flask 表格条件的父母(不带孩子)和所有父母

[英]How to filter an sqlalchemy query to all Parents w/o Children, and all Parents, who fall under conditions in a Flask Form

To begin with, I am very new to coding, so sorry in advance if it is not worth attention.首先,我对编码很陌生,如果不值得关注,请提前道歉。

I work with one to many relationship.我处理一对多的关系。 Let's say I have a Parent class and a Child class defined as follows:假设我有一个父 class 和一个子 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)

What I want to do is outerjoin the tables and display info (name, age) of all parents who:我想要做的是加入表格并显示所有父母的信息(姓名,年龄):

  1. Either do not have children要么没有孩子
  2. Or both parents and children satisfy the conditions previously entered through a Flask form (user submits a range (let's say minimal and max weight of a child and same for age)).或者父母和孩子都满足之前通过 Flask 表单输入的条件(用户提交一个范围(假设孩子的最小和最大体重以及年龄相同))。 Parent appears in the result if they have at least one child that satisfies the conditions and the parent themselves satisfy it.如果他们至少有一个满足条件的孩子并且父母自己满足条件,则父母会出现在结果中。

I succeeded in getting a query that satisfies either 1 or 2. These where the queries:我成功地获得了满足 1 或 2 的查询。这些查询:

#1 Works! #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 Also works! #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()

So how do I combine those without making too many queries (basically as efficient as possible (question mark))那么如何在不进行太多查询的情况下将它们组合起来(基本上尽可能高效(问号))

Thank you!谢谢!

EDIT 1:编辑1:

I tried using or and and conditions but it gave me an error.我尝试使用or and and条件,但它给了我一个错误。 First I will add my edited code (originally it was about galaxies and line detections)首先,我将添加我编辑的代码(最初是关于星系和线检测)

The edited query:编辑后的查询:

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))

Here is where I add kinda condition (Child.id == None) |这是我添加条件的地方(Child.id == None)| (condition1 & condition2 &...) (条件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()

And that was the error I was getting:这就是我得到的错误:

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>).

EDIT 2编辑 2

Solved the issue and made it work with the comments from @vitaliy below!解决了这个问题,并使其与下面@vitaliy 的评论一起使用!

You can just add more criteria to filter function joining then with or_ and and_ :您可以添加更多条件来filter function 然后加入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()

I'm not sure what was the intention behind or_ in your code, but now it's doing nothing because only one argument is passed to or_ function.我不确定您的代码中or_背后的意图是什么,但现在它什么也不做,因为只有一个参数传递给or_ If you meant that Child.weight or Parent.age should satisfy provided conditions, then your filter should look like this:如果您的意思是 Child.weightParent.age 应该满足提供的条件,那么您的过滤器应该如下所示:

filter(or_(Parent.age.between(age_min, age_max) Child.weight.between(weight_min, weight_max), Child.id == None))

I would also suggest making your code less redundant and more Pythonic:我还建议让您的代码减少冗余和更多 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()

Try Query.union .试试Query.union Example: verbatim from the documentaion:示例:文档中的逐字记录:

q1 = sess.query(SomeClass).filter(SomeClass.foo=='bar')
q2 = sess.query(SomeClass).filter(SomeClass.bar=='foo')

q3 = q1.union(q2)

In your case you should remove .all() before doing union .在您的情况下,您应该在执行union之前删除.all()
And i would also consider doing .order_by(..) just once on the resulting query.而且我还会考虑在结果查询上只做一次.order_by(..)
I also think that the .distinct(Parent.name) is not needed because you already do .group_by(Parent.name) which will remove duplicates.我还认为不需要 .distinct( .distinct(Parent.name) ) ,因为您已经执行.group_by(Parent.name) ,这将删除重复项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM