如何将 Python 方法链接与另一种方法结合起来

How can I combine Python method chaining with another method

我正在查询(通过 sqlalchemy)my_table,在一个列上有条件,然后在另一列中检索不同的值。很简单

selection_1 = session.query(func.distinct(my_table.col2)).\
    filter(my_table.col1 == value1)

我需要重复执行此操作才能从 my_table 的不同列中获取不同的值。

selection_2 = session.query(func.distinct(my_table.col3)).\
    filter(my_table.col1 == value1).\
    filter(my_table.col2 == value2)

selection_3 = session.query(func.distinct(my_table.col4)).\
    filter(my_table.col1 == value1).\
    filter(my_table.col2 == value2).\
    filter(my_table.col3 == value3)

以上代码有效,但由于我需要进行 6 次连续调用,所以有点失控了。我创建了一个 class 来处理方法链接:

class QueryProcessor:
    def add_filter(self, my_table_col, value):
        filter(my_table_col == value)
        return self

    def set_distinct_col(self, my_other_table_col):
        self.my_other_table_col = my_other_table_col
        return session.query(func.distinct(self.my_other_table_col))

理想情况下,我可以像

这样使用 class
selection_1 = QueryProcessor().set_distinct_col(my_table.col2).add_filter(my_table.col1, value1)
selection_2 = selection_1.set_distinct_col(my_table.col3).add_filter(my_table.col2, value2)
selection_3 = selection_2.set_distinct_col(my_table.col4).add_filter(my_table.col3, value3)

但是当我 运行

selection_1 = QueryProcessor().set_distinct_col(my_table.col2).add_filter(my_table.col1, value1)

我收到以下错误:

Traceback (most recent call last):
File " ... "
exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-20-789b26eccbc5>", line 10, in <module>
    selection_1 = QueryProcessor().set_distinct_col(my_table.col2).add_filter(my_table.col1, value1)
AttributeError: 'Query' object has no attribute 'add_filter'

非常欢迎任何帮助。

你真的不需要一个特殊的class。您现有的代码

selection_2 = session.query(func.distinct(my_table.col3)).\
    filter(my_table.col1 == value1).\
    filter(my_table.col2 == value2)

之所以有效,是因为 filter 是 returning 一个基于原始查询的 查询,但添加了一个额外的过滤器。您可以只遍历列及其对应的值,将每个旧查询替换为其后继查询。

selection2 = session.query(func.distinct(my_table.col3))
for col, val in zip([my_table.col1, my_table.col2], [value1, value2]):
    selection2 = selection2.filter(col == val)

selection_3 = session.query(func.distinct(my_table.col4))
for col, val in zip([mytable.col1, mytable.col2, mytable.col3],
                    [value1, value2, value3]):
    selection_3 = selection_3.filter(col == val)

就是说,您的代码存在的问题是 add_filter 实际上并没有调用查询的 filter 方法,也没有更新包装的查询。

class QueryProcessor:
    def set_distinct_col(self, my_other_table_col):
        self.query = session.query(func.distinct(self.my_other_table_col))
        return self

    def add_filter(self, my_table_col, value):
        self.query = self.query.filter(my_table_col == value)
        return self

不过这会带来一个问题:set_distinct_col 创建了一个新查询,因此在下面的内容中没有任何意义

selection_1 = QueryProcessor().set_distinct_col(my_table.col2).add_filter(my_table.col1, value1)
selection_2 = selection_1.set_distinct_col(my_table.col3).add_filter(my_table.col2, value2)
selection_3 = selection_2.set_distinct_col(my_table.col4).add_filter(my_table.col3, value3)

在现有实例上调用 set_distinct_col。它可以 return 新查询或现有查询,但不能同时使用(至少,如果您想进行链接,则不能)。

另外,请注意 selection_1 本身不是查询,而是 selection_1.query.

为了让您的 add_filter() 函数按预期工作,您需要 set_distinct_col() 函数对 return 自身的引用([=15= 的实例]).
session.query() return 是一个没有 add_filter() 方法的 Query 对象。 如果您执行 Query.add_filter = add_filter 之类的操作,查询可能有一个 add_filter 方法,但这是一个不好的做法,因为它修改了查询 class,所以我不建议这样做。

你正在做的是一个更好的选择。为了访问您使用 set_distinct_col() 方法创建的查询,您需要将其存储为实例变量。
下面,我通过将查询存储在实例变量 query
self.query = session.query(func.distinct(self.my_other_table_col))

中来完成此操作

然后,我将 add_filter() 方法更改为 return 本身,以允许链接更多 add_filter() 方法。

class QueryProcessor:
    def add_filter(self, my_table_col, value):
        self.query = self.query.filter(my_table_col == value)
        return self

    def set_distinct_col(self, my_other_table_col):
        self.my_other_table_col = my_other_table_col
        self.query = session.query(func.distinct(self.my_other_table_col))
        return self

您还应该知道您可以一次使用多个过滤条件,因此您实际上不需要将多个过滤器链接在一起。

session.query(db.users).filter(or_(db.users.name=='Ryan', db.users.country=='England'))

session.query(db.users).filter((db.users.name=='Ryan') | (db.users.country=='England'))

Difference between filter and filter_by in SQLAlchemy

P.S。此代码尚未经过测试