SQLAlchemy Query的变量过滤器

时间:2018-02-14 01:06:27

标签: python sqlalchemy

我在我的应用程序中添加搜索功能(使用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旨在成为对象名称的字典以及与之匹配的值。

2 个答案:

答案 0 :(得分:3)

问题在于您在和_ 连接中定义表达式。截至目前,您将每个字段与相应的值进行比较,当然每次比较都会返回false。

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

为了做到这一点,我改变了你的代码:

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

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)填充过滤器列表会爆炸在 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)最后将二进制表达式应用于您的查询,确保它以可使用的形式呈现给和_ 连词:

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

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

答案 1 :(得分:0)

一旦你得到一个绑定到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()