[英]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.