为什么很多ORM在查询中不使用纯python比较?

Why a lot of ORM does not use pure python comparison in query?

很多 ORM(比如 Django ORM 或 MongoEngine)使用 "keywords magic" 就像 id__lte=1 而不是更原生的 Model.id <= 1 (比如在 SQLAlchemy 中)。

但是为什么呢?它通过 IDE(重构、自动完成和类型检查)完全打破了静态分析。

可能有一些我看不到的原因?或者只是没有那么多人认为 Model.id <= 1id__lte=1 好?


Django ORM 示例:

Blog.objects.get(name="Cheddar Talk", user_reviews__lte=5)

SQLAlchemy 示例

session.query(Blog).filter(Blog.name == "Cheddar Talk", Blog.user_reviews <= 5)

像 SQLAlchemy 使用的 "expression tree" 魔术的一个问题是,并非所有运算符都可以重载以捕获表达式。

比如,你想让it <= 1it捕获,从而在SQL中变成WHERE it <= 1表达式?很简单:

class Magic:
    def __le__(self, other):
        return MagicExpression(operator.le, self, other)

您为这些 MagicExpression 个对象构建了一棵树,将那棵树变成 SQL 查询的代码非常简单。


但是如果您还想捕获 it in other_query 并将其转换为 SQL INBETWEEN 表达式怎么办?那是行不通的。一方面, __contains__ 在另一件事上被调用,而不是你的魔法。而且,更糟糕的是,任何 __contains__ returns 都被解释器转换为 bool。所以,如果你试试这个:

class Magic:
    def __contains__(self, other):
        return MagicExpression(operator.contains, other, self)

… 然后尝试 it in it2,你将得到 True(或任何 MagicExpression.__bool__ returns),这不是你可以转换成的东西SQL INBETWEEN 稍后。

如果您尝试 and 将两个条件放在一起会怎样?好吧,您甚至不能重载 and 运算符。如果左边 MagicExpression 是假的,你就会得到它;否则,你会得到正确的。

此外,当然还有一个简单的问题,即有时您需要一个甚至不使用任何魔法的静态测试,至少出于调试目的。 "OK, something is wrong in my test on that column… what if I just test against 0 and see what the query returns?" 但是如果 count0 都是纯 Python 整数,则不能将 count > 0 变成 MagicExpression


有些人认为这些限制是可以接受的。也许你可以设计一些东西来使用 &| 代替 andor,并让人们写 "uglier" 版本而不是 in, 等等。所以很多查询最终都很简单,但有些最终是简单和丑陋的混合体——总比所有的都丑陋要好,对吧?问题是它可能有点像磁铁——很容易忘记限制并使用 andin,最后得到一个看起来正确但不正确的表达式做你想做的。

因此,其他人认为这是不可接受的,并且只要求您按照 "ugly" 的方式做所有事情,这样就不会想编写 hard-to-debug 损坏的查询。

当然,有些人会在列表理解之上构建 LINQ-esque DSL,并使用导入挂钩将其转换为有效的 SQL-building 或 ORM-using Python。