繁体   English   中英

SQLAlchemy查询的变量过滤器

[英]Variable filter for SQLAlchemy Query

我在我的应用程序(使用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旨在作为对象名称和与其匹配的值的字典。

问题在于您在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 = list()

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

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

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

一旦您将查询对象绑定到SqlAlquemy中的表(即上述代码中的session.query(Archive)返回的内容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()

暂无
暂无

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

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