使用Peewee的NOT IN运算符

16
文档显示这里如何使用IN运算符,但我找不到如何使用NOT IN运算符。
如果我输入not <<,会出现语法错误。
如果我输入not <FieldName> <<,会得到WHERE False而不是子查询,例如WHERE (<FieldName> NOT IN (SELECT ...
以下是文档示例的输出。第一个是正确的,第二个和第三个是错误的。
>>> Tweet.select().where(Tweet.user << a_users).sql()
('SELECT t1."id", t1."user_id", t1."message", t1."created_date", t1."is_published" FROM "tweet" AS t1 WHERE (t1."user_id" IN (SELECT t2."id" FROM "user" AS t2 WHERE (Lower(Substr(t2."username", ?, ?)) = ?)))', [1, 1, 'a'])
>>> Tweet.select().where(not Tweet.user << a_users).sql()
('SELECT t1."id", t1."user_id", t1."message", t1."created_date", t1."is_published" FROM "tweet" AS t1 WHERE ?', [False])
>>> Tweet.select().where(Tweet.user not << a_users).sql()
SyntaxError: invalid syntax

“not in” 字面上是一个名为 “not in” 的单一运算符,它并不意味着你可以在任何其他运算符之前加上 “not”。Peewee 重新解释 << 以表示 SQL 中的 IN 并不意味着它可以更改 Python 语法。 - abarnert
@abarnert 我知道... 所以我问的。 - stenci
3个回答

33

简单:

Tweet.select().where(Tweet.user.not_in(a_users))

如果要表达略微不同的语义(即 NOT (x in y)),与 (x NOT IN y) 相反:

Tweet.select().where(~(Tweet.user << a_users))

1
是的,这很有道理,~是可重载的,不需要返回布尔值。但似乎没有任何文档记录它。 - kindall
Kindall,请你更新或删除你的回答?它是不正确的,可能会给其他读者带来困惑。 - coleifer
有“存在”运算符吗?我觉得你们支持窗口函数,却不支持“not”和“exists”,感觉很奇怪。 - ypercubeᵀᴹ
fn.EXISTS() 是你可以实现这个的方法。 - coleifer
你不能覆盖notin,因为Python解释器会将返回值强制转换为布尔值。显然我本来更喜欢那样做。 - coleifer
我原本认为我需要一个一元否定运算符,然后我尝试了!。我没有想到还可以用~。谢谢。 - stenci

4

我知道这是一个“死帖子”,但这个问题在谷歌上搜索peewee not in时排名第一,所以我想在这里补充一下:

你也可以使用文档中描述的not_in方法:

Tweet.select().where(Tweet.user.not_in(a_users))

对我而言,这种方式比~ ... << 语法结构更易读。


1
嘿,谢谢你提到这个!我已经更新了我的答案,包括你的编辑,希望更多的人能看到它。 - coleifer

1
这与Peewee无关。Peewee只是为了自己的目的使用了一些Python运算符。 << 通常是一个数值运算符,取它的逻辑否定毫无意义。因此,not << 永远不是有效的Python语法。
你的第二个例子很接近,但 not 只适用于 Tweet.user (not 的优先级高于 <<)。添加一些括号,你会得到:
Tweet.select().where(not (Tweet.user << a_users)).sql()

现在这还是不对的,正如你已经发现的一样(读者:请参考评论以获取更多讨论)。not返回一个布尔值,这不是想要的,并且也不会起作用。Peewee使用~运算符来代替这个操作符;请查看@coleifer的答案。

如果你想知道为什么Peewee会使用<<而不是只使用in(在这种情况下,你可以在这里使用not in);in运算符必须返回一个bool,而不是查询对象或其他任何东西。(如果它不返回一个bool,那么取决于你的Python实现和版本;它可能是一个错误,或者它可能被转换为bool,但无论哪种方式,它都不是非常有用的...) - abarnert
我不理解你的回答。据我理解,这些运算符是由Peewee用于创建SQL命令的,它们不是由Python解释的。实际上,如果您尝试使用您的表达式,您将得到一个... WHERE False,就像我的第二个例子中没有括号一样。 - stenci
它们不能不被Python解释。Peewee提供了特殊的Python对象,覆盖了运算符,如<<来构建SQL查询。换句话说,在Peewee中,没有任何东西会看到Tweet.user << a_users并对其进行处理,所有这些都是由Tweet.user上的方法完成的。结果是一种领域特定语言(DSL),它与Python无缝地配合工作,因为它就是Python。不幸的是,我对你的问题的解决方案猜错了。 - kindall
看了一下Peewee文档中的查询操作符,似乎不支持not~或名为not_unary_not的方法或函数...这不可能是真的,对吧?如果是的话,添加用户定义的操作符可以告诉你如何实现。 - abarnert
1
@stenci:另外,我会向Peewee提交一个错误报告,指出它要么没有编写“NOT”查询的方法,要么(如果有)从文档中找到该方法太困难了... - abarnert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接