过滤方法表现异常
Filter method is behaving unexpectedly
我正在尝试将类型提示引入现有代码库,但是当我尝试输入查询时 运行 遇到了问题。
from sqlalchemy.orm.query import Query
class DbContext:
def __init__(self, db_host, db_port, db_name, db_user, db_password):
engine = create_engine(...)
session = sessionmaker(bind=engine)
self.Session: Session = session(bind=engine)
...
def fetch(context: DbContext, filters: ...):
sub_query: Query = context.Session.query(...)
在我添加类型提示之前,动态过滤只是一个问题:
if filters.name is not None:
sub_query = sub_query.filter(
Person.name.ilike(f"%{filters.name}%"))
但是,现在提示我收到此错误:
Expression of type "None" cannot be assigned to declared type "Query"
果然filter
出现在return None
:
(method) filter: (*criterion: Unknown) -> None
我导航到 the source,看来该方法确实没有 return 任何东西。
def filter(self, *criterion):
for criterion in list(criterion):
criterion = expression._expression_literal_as_text(criterion)
criterion = self._adapt_clause(criterion, True, True)
if self._criterion is not None:
self._criterion = self._criterion & criterion
else:
self._criterion = criterion
显然某处断开连接,因为将 None
分配给 sub_query
会导致提示警告的错误,但我需要执行分配才能使过滤真正起作用:
# Does NOT work, filtering is not applied
if filters.name is not None:
sub_query.filter(
Person.name.ilike(f"%{filters.name}%"))
# Works but Pylance complains
if filters.name is not None:
sub_query = sub_query.filter(
Person.name.ilike(f"%{filters.name}%"))
这是我第一次涉足 Python,希望得到一些关于这里发生的事情的指导!
您遗漏了两件事:
- 您需要为 SQLAlchemy 安装 typing stubs。
Query.filter()
方法有一个装饰器,用于定义什么是 returned。
SQLAlchemy 的输入存根
您想安装 sqlalchemy-stubs
project,它为 SQLAlchemy API 提供存根。
请注意,即使安装了这个存根,您仍然会看到 Pyright(支持 Pylance 扩展的检查工具)的问题,因为静态存根不能完全代表某些组件的动态特性SQLAlchemy API 的部分,例如模型列定义(例如,如果您的 Person
模型有一个名为 name
的列,用 name = Column(String)
定义,那么存根无法分辨Pyright name
将是一个字符串)。 sqlalchemy-stubs
项目包含一个用于 mypy 类型检查器的插件,以更好地处理动态部分,但此类插件不能与其他类型检查器一起使用。
安装存根后,Pylance 可以告诉您有关 filter
:
Query.filter()
装饰器详细信息
Query.filter()
方法实现实际上并不是对原始实例对象进行操作;它已被注释为 decorator:
@_generative(_no_statement_condition, _no_limit_offset)
def filter(self, *criterion):
...
@_generative(...)
部分在这里很重要; definition of the decorator factory 显示 filter()
方法基本上被此 包装方法 :
取代
def generate(fn, *args, **kw):
self = args[0]._clone()
for assertion in assertions:
assertion(self, fn.__name__)
fn(self, *args[1:], **kw)
return self
这里,fn
是原始的filter()
方法定义,args[0]
是对self
的引用,是初始的Query
实例。所以 self
被调用 self._clone()
取代(基本上,创建一个新实例并复制属性),它运行声明的断言(这里, _no_statement_condition
和 _no_limit_offset
是此类断言),在 运行 原始函数 之前 。
因此,filter()
函数的作用是 就地更改克隆的实例 ,因此不必 return 任何东西;这是由 generate()
包装器处理的。正是这种用实用程序包装器交换方法的技巧让 Pyright 误以为 None
是 returned,但安装存根后它知道另一个 Query
实例是 returned相反。
我正在尝试将类型提示引入现有代码库,但是当我尝试输入查询时 运行 遇到了问题。
from sqlalchemy.orm.query import Query
class DbContext:
def __init__(self, db_host, db_port, db_name, db_user, db_password):
engine = create_engine(...)
session = sessionmaker(bind=engine)
self.Session: Session = session(bind=engine)
...
def fetch(context: DbContext, filters: ...):
sub_query: Query = context.Session.query(...)
在我添加类型提示之前,动态过滤只是一个问题:
if filters.name is not None:
sub_query = sub_query.filter(
Person.name.ilike(f"%{filters.name}%"))
但是,现在提示我收到此错误:
Expression of type "None" cannot be assigned to declared type "Query"
果然filter
出现在return None
:
(method) filter: (*criterion: Unknown) -> None
我导航到 the source,看来该方法确实没有 return 任何东西。
def filter(self, *criterion):
for criterion in list(criterion):
criterion = expression._expression_literal_as_text(criterion)
criterion = self._adapt_clause(criterion, True, True)
if self._criterion is not None:
self._criterion = self._criterion & criterion
else:
self._criterion = criterion
显然某处断开连接,因为将 None
分配给 sub_query
会导致提示警告的错误,但我需要执行分配才能使过滤真正起作用:
# Does NOT work, filtering is not applied
if filters.name is not None:
sub_query.filter(
Person.name.ilike(f"%{filters.name}%"))
# Works but Pylance complains
if filters.name is not None:
sub_query = sub_query.filter(
Person.name.ilike(f"%{filters.name}%"))
这是我第一次涉足 Python,希望得到一些关于这里发生的事情的指导!
您遗漏了两件事:
- 您需要为 SQLAlchemy 安装 typing stubs。
Query.filter()
方法有一个装饰器,用于定义什么是 returned。
SQLAlchemy 的输入存根
您想安装 sqlalchemy-stubs
project,它为 SQLAlchemy API 提供存根。
请注意,即使安装了这个存根,您仍然会看到 Pyright(支持 Pylance 扩展的检查工具)的问题,因为静态存根不能完全代表某些组件的动态特性SQLAlchemy API 的部分,例如模型列定义(例如,如果您的 Person
模型有一个名为 name
的列,用 name = Column(String)
定义,那么存根无法分辨Pyright name
将是一个字符串)。 sqlalchemy-stubs
项目包含一个用于 mypy 类型检查器的插件,以更好地处理动态部分,但此类插件不能与其他类型检查器一起使用。
安装存根后,Pylance 可以告诉您有关 filter
:
Query.filter()
装饰器详细信息
Query.filter()
方法实现实际上并不是对原始实例对象进行操作;它已被注释为 decorator:
@_generative(_no_statement_condition, _no_limit_offset)
def filter(self, *criterion):
...
@_generative(...)
部分在这里很重要; definition of the decorator factory 显示 filter()
方法基本上被此 包装方法 :
def generate(fn, *args, **kw):
self = args[0]._clone()
for assertion in assertions:
assertion(self, fn.__name__)
fn(self, *args[1:], **kw)
return self
这里,fn
是原始的filter()
方法定义,args[0]
是对self
的引用,是初始的Query
实例。所以 self
被调用 self._clone()
取代(基本上,创建一个新实例并复制属性),它运行声明的断言(这里, _no_statement_condition
和 _no_limit_offset
是此类断言),在 运行 原始函数 之前 。
因此,filter()
函数的作用是 就地更改克隆的实例 ,因此不必 return 任何东西;这是由 generate()
包装器处理的。正是这种用实用程序包装器交换方法的技巧让 Pyright 误以为 None
是 returned,但安装存根后它知道另一个 Query
实例是 returned相反。