如何解决过滤方法的行为异常
我正在尝试将类型提示引入现有代码库,但在尝试键入查询时遇到了问题。
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}%"))
“None”类型的表达式不能分配给声明的“Query”类型
果然,filter
似乎返回了 None
:
(方法)过滤器:(*标准:未知)-> 无
我导航到 the source,看起来该方法确实没有返回任何内容。
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()
方法有一个装饰器,用于定义返回的内容。
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()
函数的作用是改变克隆的实例,因此不必返回任何内容;这由 generate()
包装器处理。正是这种用实用程序包装器换出方法的技巧让 Pyright 误以为返回了 None
,但安装存根后,它知道返回的是另一个 Query
实例。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。