【问题标题】:Variable filter for SQLAlchemy QuerySQLAlchemy 查询的变量过滤器
【发布时间】:2018-02-14 01:06:26
【问题描述】:

我正在向我的应用程序(使用 PyQt5 创建)添加搜索功能,允许用户搜索数据库中的存档表。我提供了适用的字段供用户选择与行匹配。鉴于其他字段将是空字符串,我在查询过滤器仅使用用户提供的内容时遇到了一些问题。

这是我目前所拥有的:

 def search_for_order(pierre):
    fields = {'archive.pat.firstname': pierre.search_firstname.text(),
              'archive.pat.lastname': pierre.search_lastname.text(),
              'archive.pat.address': pierre.search_address.text(),
              'archive.pat.phone': pierre.search_phone.text(),
              'archive.compound.compname': pierre.search_compname.text(),
              'archive.compound.compstrength': pierre.search_compstrength.text(),
              'archive.compound.compform': pierre.search_compform.currentText(),
              'archive.doc.lastname': pierre.search_doctor.text(),
              'archive.clinic.clinicname': pierre.search_clinic.text()
             }

    filters = {}

    for field, value in fields.items():
        if value is not '':
            filters[field] = value

     query = session.query(Archive).join(Patient, Prescribers, Clinic, Compound)\
             .filter(and_(field == value for field, value in filters.items())).all()

fields 字典收集搜索表单中所有字段的值。其中一些将是空白的,从而导致空字符串。 filters 旨在成为对象名称和与之匹配的值的字典。

【问题讨论】:

    标签: python sqlalchemy


    【解决方案1】:

    问题在于您对 and_ 连词中表达式的定义。到目前为止,您正在将每个字段与相应的值进行比较,当然每次比较都会返回 false。

    要正确填充 and_ 连接,您必须创建一个 sqlalchemy 调用 BinaryExpression 对象的列表。

    为了做到这一点,我会像这样更改您的代码:

    1) 首先在字段的定义中使用对表类的实际引用:

    fields = {
        (Patient, 'firstname'): pierre.search_firstname.text(),
        (Patient, 'lastname'): pierre.search_lastname.text(),
        (Patient, 'address'): pierre.search_address.text(),
        (Patient, 'phone'): pierre.search_phone.text(),
        (Compound, 'compname'): pierre.search_compname.text(),
        (Compound, 'compstrength'): pierre.search_compstrength.text(),
        (Compound, 'compform'): pierre.search_compform.currentText(),
        (Prescribers, 'lastname'): pierre.search_doctor.text(),
        (Clinic, 'clinicname'): pierre.search_clinic.text()
    }
    

    2) 将filters定义为列表而不是字典:

    filters = list()
    

    3) 要填充过滤器列表,请在 fields 字典中分解用作键的表和字段名的元组,并添加值以再次创建元组,但现在包含三个元素。将每个新创建的元组附加到过滤器列表中:

    for table_field, value in fields.items():
        table, field = table_field
        if value:
            filters.append((table, field, value))
    

    4) 现在将创建的过滤器定义列表转换​​为 sqlalchemy 可用的 BinaryExpression 对象列表:

    binary_expressions = [getattr(table, attribute) == value for table, attribute, value in filters]
    

    5) 最后将二进制表达式应用于您的查询,确保它以可消耗的形式呈现给 and_ 连词:

    query = session.query(Archive).join(Patient, Prescribers, Clinic, Compound)\
             .filter(and_(*binary_expressions)).all()
    

    我无法在您的配置中测试该解决方案,但使用我的环境进行的类似测试是成功的。

    【讨论】:

    • 两个答案都在语句中产生WHERE false = 1,导致一个空列表。
    • 开个玩笑,语法错误。这似乎运作良好,但我不完全明白为什么。
    • 您能具体说明您不理解的部分吗?我试图解释发生了什么,但显然这还不够。
    【解决方案2】:

    一旦你得到一个绑定到 SqlAlquemy 中的表的查询对象——即上面代码中session.query(Archive) 返回的内容——调用该对象的某些方法将返回一个新的、修改后的查询,其中该过滤器是已经申请了。

    因此,我首选的组合多个 and 过滤器的方法是从裸查询开始,迭代要使用的过滤器,然后为每个过滤器添加一个新的 .filter 调用并重新分配查询:

    query = session.query(Archive).join(Patient, Prescribers, Clinic, Compound)
    for field, value in filters.items():
        query = query.filter(field == value)
    results = query.all()
    

    按照您的意图使用and_or_ 也可以工作 - 在您的示例中,唯一缺少的是*。如果生成器表达式之前没有*,它将作为第一个(也是唯一的)参数传递给and_。使用前缀*,迭代器中的所有元素都被解包,每个元素都作为参数传递:

    ...
    .filter(and_(*(field == value for field, value in filters.items()))).all()
    

    【讨论】:

    • 这会产生false =1的WHERE子句
    • 我没有写过你的过滤器字典必须由真实的字段对象组成,从模型类中挑选出来——另一个答案涉及这个。我想你在我写这个答案之前就知道了。
    猜你喜欢
    • 2015-01-15
    • 1970-01-01
    • 1970-01-01
    • 2021-08-10
    • 2017-03-24
    • 2019-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多