简体   繁体   English

SQLAlchemy 查询所有孩子匹配过滤器的父母

[英]SQLAlchemy query parents with ALL children matching a filter

I'm trying to create a sqlalchemy query (flask-sqlalchemy) that only returns parent objects if ALL children match the query instead of ANY.我正在尝试创建一个 sqlalchemy 查询(flask-sqlalchemy),如果所有子项都匹配查询而不是 ANY,则该查询仅返回父对象。 IE IE

(Parent
 .query
 .join(Child)
 .filter(Child.column == True)
 ).all()

Will return all of the parent objects that have any child with column == True , how do I write it so that all children must have column == True ?将返回所有具有column == True的子对象的所有父对象,我该如何编写它以使所有子对象都必须具有column == True

------------------------------- update ------------------------------ - - - - - - - - - - - - - - - - 更新 - - - - - - - - - ------------

The above portion was abstracted from this actual code.上面的部分是从这个实际代码中抽象出来的。 Where在哪里

Parent == Student and Child == StudentApplication家长 == 学生和孩子 == StudentApplication

The model heirarchy goes: model 层次结构如下:

User ---> Students ---> StudentApplication用户 ---> 学生 ---> StudentApplication

I'm trying to retrieve users, who have students, who's application has not been submitted, while ignoring users who have at least 1 student with a submitted application.我正在尝试检索有学生但尚未提交申请的用户,同时忽略至少有 1 名学生提交申请的用户。

def _active_student_query(statement=None):
    import sqlalchemy as sa
    statement = statement or _active_user_query()
    statement = statement.join(Student).filter(Student.suspended == sa.false())
    return statement

def users_without_submitted_applications(exclude_contacted=False):
    import sqlalchemy as sa
    from sqlalchemy.orm.util import AliasedClass

# AppAlias = AliasedClass(StudentApplication)

has_any_approved_app = sa.exists(
    sa.select([])
        .select_from(Student)
        .where((StudentApplication.student_id == Student.id) &
               (StudentApplication.flags.op('&')(AppFlags.SUBMITTED) > 0),
               )
)

statement = _active_student_query()
statement = (statement
             # .join(StudentApplication)
             # .filter(User.students.any())
             # has a student and no submitted applications
             .filter(~has_any_approved_app)
             # .filter(StudentApplication.flags.op('&')(AppFlags.SUBMITTED) == 0)
             )
if exclude_contacted:
    statement = (statement
                 .join(AlertPreferences)
                 .filter(AlertPreferences.marketing_flags.op('&')(MarketingFlags.NO_APP_PING) == 0)
                 )

from lib.logger import logger
logger.info(statement.sql)
return statement

And here's the SQL that it produces这是它生产的 SQL

SELECT users.is_active, users.flags, users.created_on, users.updated_on, users.id
FROM users JOIN student ON users.id = student.user_id 
WHERE users.is_active = true AND student.suspended = false AND NOT (EXISTS (SELECT  
FROM student_application 
WHERE student_application.student_id = student.id AND (student_application.flags & %(flags_1)s) > %(param_1)s))

If you're looking for a parent where all children have column = TRUE , then that is equivalent to all parents where no child has column = FALSE .如果您正在寻找所有孩子都有column = TRUE的父母,那么这相当于没有孩子有column = FALSE的所有父母。 If you use a JOIN, you'd run into the problem of having one row per child, instead of per parent.如果您使用 JOIN,您会遇到每个孩子而不是每个父母拥有一行的问题。 Therefore, I'd recommend using WHERE EXISTS() instead.因此,我建议改用WHERE EXISTS() A query like this would solve the problem:像这样的查询可以解决问题:

SELECT *
FROM parent
WHERE NOT EXISTS(
    SELECT
    FROM child
    WHERE child.parent_id = parent.id
      AND child.column = FALSE
)

and in Flask-SQLAlchemy this becomes:在 Flask-SQLAlchemy 中,这变成:

import sqlalchemy as sa
has_any_false_children = sa.exists(
  sa.select([])
  .select_from(Child)
  .where((Child.parent_id == Parent.id) &
         (Child.column == sa.false()))
)

Parent.query.filter(~has_any_false_children)

Update更新

Since you're talking about User s where all Student s have all StudentApplication s completed, I think it should become既然你在谈论User s,所有Student s 都完成了所有StudentApplication s,我认为它应该变成

student_has_any_non_approved_app = sa.exists(
    sa.select([])
      .select_from(StudentApplication)
      .where((StudentApplication.student_id == Student.id) &
             (StudentApplication.flags.op('&')(AppFlags.SUBMITTED) > 0) &
             ((StudentApplication.flags.op('&')(AppFlags.APPROVED) == 0) |
              (StudentApplication.flags.op('&')(AppFlags.PARTIALLY_APPROVED) == 0)))
)

user_has_any_non_approved_students = sa.exists(
    sa.select([])
      .select_from(Student)
      .where((Student.user_id == User.id) &
                         (Student.suspended == sa.false()) &
             student_has_any_non_approved_app)
)

statement = (
        _active_user_query()
        .filter(User.students.any())
      # has a student and no submitted applications
      .filter(~user_has_any_non_approved_students)
)

This will return all users.这将返回所有用户。 If you then want the students of that user, I'd put that into a separate query - and apply marketing flags there as well.如果您随后想要该用户的学生,我会将其放入单独的查询中 - 并在那里应用营销标志。

statement = Student.query.filter(
    Student.user_id.in_(user_ids),
    Student.suspended == sa.false()
)

if exclude_contacted:
    statement = (statement
                 .join(AlertPreferences)
                 .filter(AlertPreferences.marketing_flags.op('&')(MarketingFlags.NO_APP_PING) == 0)
                 )

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

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