SQLAlchemy 最新的版本已经是0.9.1了,不过有人报告说Uliweb在使用这个版本时有问题,get()不到数据。后来查了一下,发现生成的查询条件多了一个NULL,比如:
Blog.get(1)
SELECT todo.title, todo.post_date, todo.finished, todo.id FROM todo
WHERE todo.id = 1 AND NULL LIMIT 1 OFFSET 0;
在条件中多了一个NULL,很奇怪,換成0.8.X就好了。然后我仔细查了一下,在Uliweb中,get()会调用filter,而filter本身可以传入多个条件,因此需要将条件以与的方式合并,因此代码是:
cond = None
for c in condition:
if c is not None:
cond = c & cond
因此,最后条件会和None相与。
然后就发现是 and_(&)
的处理和以前不同的。以前的代码是在sqlalchemy.sql.expression中的ClauseList的类中,以0.8.3版本为例是在3325行左右,代码为:
if self.group_contents:
self.clauses = [
_literal_as_text(clause).self_group(against=self.operator)
for clause in clauses if clause is not None]
else:
self.clauses = [
_literal_as_text(clause)
for clause in clauses if clause is not None]
可以看到,在for中已经装饰None的条件过滤了,所以对于条件是None的会自动过滤掉。
但是到了0.9.1中,代码发生了很大的变化,这块代码都移到了elements.py中了,而且真正的and_的处理从ClauseList中移到了BooleanClauseList中,并且代码处理也有所不同:
convert_clauses = []
clauses = util.coerce_generator_arg(clauses)
for clause in clauses:
clause = _literal_as_text(clause)
if isinstance(clause, continue_on):
continue
elif isinstance(clause, skip_on):
return clause.self_group(against=operators._asbool)
convert_clauses.append(clause)
没有了对None的过滤。所以造成了None在0.9.1中与0.8.X中不兼容的情况。不知道这算不算一个Bug。不过我已经把uliweb中的cond = None
改为了 cond = ''
。这样结果在0.8.X中还是在0.9.X中都是对的。
所以如果你是在0.9.X中使用None条件,建议改为 true()。true 可以从 sqlalchemy.sql 中导出。
update
如果你希望0.9.x和0.8.x对None的处理一样,我找到一个打补丁的方法:
from sqlalchemy.sql.compiler import SQLCompiler
def visit_null(self, expr, **kw):
return ''
setattr(SQLCompiler, 'visit_null', visit_null)
这样 NULL
就不会出现。你也可以考虑在这个地方加一个抛异常,强制用户修改,这也是一个办法。