查询算子

Peewee支持以下比较类型:

比较

意义

==

x等于y

<

x小于y

<=

x小于或等于y

>

x大于y

>=

x大于或等于y

!=

x不等于y

<<

x代表y,其中y代表列表或查询

>>

x是y,其中y是无/空

%

x类似y,其中y可能包含通配符

**

x i类似y,其中y可能包含通配符

^

X异或Y

~

一元否定(例如,不是x)

由于要重写的运算符不足,因此还有一些其他查询操作可用作方法:

方法

意义

.in_(value)

在查找中(与 <<

.not_in(value)

不在查找中。

.is_null(is_null)

为空或不为空。接受布尔参数。

.contains(substr)

子字符串的通配符搜索。

.startswith(prefix)

搜索以开头的值 prefix .

.endswith(suffix)

搜索以结尾的值 suffix .

.between(low, high)

在哪里搜索 low <= value <= high

.regexp(exp)

正则表达式匹配(区分大小写)。

.iregexp(exp)

正则表达式匹配(不区分大小写)。

.bin_and(value)

二进制和

.bin_or(value)

二进制或

.concat(other)

使用连接两个字符串或对象 || .

.distinct()

为非重复选择标记列。

.collate(collation)

使用给定的排序规则指定列。

.cast(type)

将列的值强制转换为给定的类型。

要使用逻辑运算符组合子句,请使用:

算符

意义

例子

&

AND

(User.is_active == True) & (User.is_admin == True)

| (管道)

OR

(User.is_admin) | (User.is_superuser)

~

非(一元否定)

~(User.username.contains('admin'))

下面是如何使用这些查询运算符:

# Find the user whose username is "charlie".
User.select().where(User.username == 'charlie')

# Find the users whose username is in [charlie, huey, mickey]
User.select().where(User.username.in_(['charlie', 'huey', 'mickey']))

# Find users whose salary is between 50k and 60k (inclusive).
Employee.select().where(Employee.salary.between(50000, 60000))

Employee.select().where(Employee.name.startswith('C'))

Blog.select().where(Blog.title.contains(search_string))

下面是如何组合表达式。比较可以任意复杂。

备注

请注意,实际比较用括号括起来。python的运算符优先级要求将比较用括号括起来。

# Find any users who are active administrations.
User.select().where(
  (User.is_admin == True) &
  (User.is_active == True))

# Find any users who are either administrators or super-users.
User.select().where(
  (User.is_admin == True) |
  (User.is_superuser == True))

# Find any Tweets by users who are not admins (NOT IN).
admins = User.select().where(User.is_admin == True)
non_admin_tweets = Tweet.select().where(Tweet.user.not_in(admins))

# Find any users who are not my friends (strangers).
friends = User.select().where(User.username.in_(['charlie', 'huey', 'mickey']))
strangers = User.select().where(User.id.not_in(friends))

警告

尽管您可能会尝试使用python的 inandornot 查询表达式中的运算符,这些**将不起作用。**的返回值 in 表达式总是强制为布尔值。同样地, andornot 所有参数都将其视为布尔值,不能重载。

所以请记住:

  • 使用 .in_().not_in() 而不是 innot in

  • 使用 & 而不是 and

  • 使用 | 而不是 or

  • 使用 ~ 而不是 not

  • 使用 .is_null() 而不是 is None== None .

  • 使用逻辑运算符时,不要忘记将比较结果括在括号中。

有关更多示例,请参见 表达 部分。

备注

like和ilike with sqlite

因为sqlite的 LIKE 操作默认不区分大小写,peewee将使用sqlite GLOB 区分大小写搜索的操作。glob操作使用星号作为通配符,而不是通常的百分号。如果您使用的是sqlite,并且需要区分大小写的部分字符串匹配,请记住使用星号作为通配符。

三值逻辑

因为SQL处理的方式 NULL 有一些特殊操作可用于表达:

  • IS NULL

  • IS NOT NULL

  • IN

  • NOT IN

虽然可以使用 IS NULLIN 带否定运算符的运算符( ~ )有时为了获得正确的语义,需要显式地使用 IS NOT NULLNOT IN .

最简单的使用方法 IS NULLIN 是使用运算符重载:

# Get all User objects whose last login is NULL.
User.select().where(User.last_login >> None)

# Get users whose username is in the given list.
usernames = ['charlie', 'huey', 'mickey']
User.select().where(User.username << usernames)

如果不喜欢运算符重载,可以改为调用Field方法:

# Get all User objects whose last login is NULL.
User.select().where(User.last_login.is_null(True))

# Get users whose username is in the given list.
usernames = ['charlie', 'huey', 'mickey']
User.select().where(User.username.in_(usernames))

要否定上述查询,可以使用一元否定,但是为了正确的语义,可能需要使用特殊的 IS NOTNOT IN 运营商:

# Get all User objects whose last login is *NOT* NULL.
User.select().where(User.last_login.is_null(False))

# Using unary negation instead.
User.select().where(~(User.last_login >> None))

# Get users whose username is *NOT* in the given list.
usernames = ['charlie', 'huey', 'mickey']
User.select().where(User.username.not_in(usernames))

# Using unary negation instead.
usernames = ['charlie', 'huey', 'mickey']
User.select().where(~(User.username << usernames))

添加用户定义的运算符

因为我用光了python操作符来重载,比如peewee中有一些缺少的操作符。 modulo . 如果您发现需要支持上表中没有的运算符,则很容易添加自己的运算符。

下面是如何添加对 modulo 在SQLite中:

from peewee import *
from peewee import Expression  # The building block for expressions.

def mod(lhs, rhs):
    # Note: this works with Sqlite, but some drivers may use string-
    # formatting before sending the query to the database, so you may
    # need to use '%%' instead here.
    return Expression(lhs, '%', rhs)

现在,您可以使用这些自定义运算符来构建更丰富的查询:

# Users with even ids.
User.select().where(mod(User.id, 2) == 0)

有关更多示例,请查看 playhouse.postgresql_ext 模块,因为它包含许多特定于PostgreSQL的hstore的操作符。

表达

Peewee的设计目的是提供一种简单、富有表现力和pythonic的方式来构造SQL查询。本节将快速概述一些常见的表达式类型。

可以组合两种主要类型的对象来创建表达式:

  • Field 实例

  • SQL聚合和函数使用 fn

我们将假设一个简单的“用户”模型,其中包含用户名和其他内容的字段。看起来是这样的:

class User(Model):
    username = CharField()
    is_admin = BooleanField()
    is_active = BooleanField()
    last_login = DateTimeField()
    login_count = IntegerField()
    failed_logins = IntegerField()

比较使用 查询算子

# username is equal to 'charlie'
User.username == 'charlie'

# user has logged in less than 5 times
User.login_count < 5

比较可以使用 bitwise or. 运算符优先级由python控制,比较可以嵌套到任意深度:

# User is both and admin and has logged in today
(User.is_admin == True) & (User.last_login >= today)

# User's username is either charlie or charles
(User.username == 'charlie') | (User.username == 'charles')

比较也可用于函数:

# user's username starts with a 'g' or a 'G':
fn.Lower(fn.Substr(User.username, 1, 1)) == 'g'

我们可以做一些相当有趣的事情,因为表达式可以与其他表达式进行比较。表达式还支持算术运算:

# users who entered the incorrect more than half the time and have logged
# in at least 10 times
(User.failed_logins > (User.login_count * .5)) & (User.login_count > 10)

表达式允许我们这样做 原子更新:

# when a user logs in we want to increment their login count:
User.update(login_count=User.login_count + 1).where(User.id == user_id)

表达式可用于查询的所有部分,因此请进行实验!

行值

许多数据库支持 row values, which are similar to Python tuple 物体。在peewee中,可以通过 Tuple . 例如,

# If for some reason your schema stores dates in separate columns ("year",
# "month" and "day"), you can use row-values to find all rows that happened
# in a given month:
Tuple(Event.year, Event.month) == (2019, 1)

行值的更常见用法是在单个表达式中比较子查询中的多个列。还有其他方法来表示这些类型的查询,但是行值可以提供一种简洁易读的方法。

例如,假设我们有一个表“eventlog”,其中包含事件类型、事件源和一些元数据。我们还有一个“incidentlog”,它有事件类型、事件源和元数据列。我们可以使用行值将事件与某些事件关联起来:

class EventLog(Model):
    event_type = TextField()
    source = TextField()
    data = TextField()
    timestamp = TimestampField()

class IncidentLog(Model):
    incident_type = TextField()
    source = TextField()
    traceback = TextField()
    timestamp = TimestampField()

# Get a list of all the incident types and sources that have occured today.
incidents = (IncidentLog
             .select(IncidentLog.incident_type, IncidentLog.source)
             .where(IncidentLog.timestamp >= datetime.date.today()))

# Find all events that correlate with the type and source of the
# incidents that occured today.
events = (EventLog
          .select()
          .where(Tuple(EventLog.event_type, EventLog.source).in_(incidents))
          .order_by(EventLog.timestamp))

表达这种类型查询的其他方法是使用 joinjoin on a subquery . 上面的例子只是给你和你的想法 Tuple 可能会被使用。

当从子查询派生新数据时,还可以使用行值更新表中的多个列。例如,请参见 here .

SQL函数

SQL函数,比如 COUNT()SUM() ,可以用 fn() 帮手:

# Get all users and the number of tweets they've authored. Sort the
# results from most tweets -> fewest tweets.
query = (User
         .select(User, fn.COUNT(Tweet.id).alias('tweet_count'))
         .join(Tweet, JOIN.LEFT_OUTER)
         .group_by(User)
         .order_by(fn.COUNT(Tweet.id).desc()))
for user in query:
    print('%s -- %s tweets' % (user.username, user.tweet_count))

这个 fn helper公开任何SQL函数,就像它是一个方法一样。参数可以是字段、值、子查询,甚至是嵌套函数。

嵌套函数调用

假设您需要获取用户名以开头的所有用户的列表 a. 有两种方法可以做到这一点,但一种方法可能是使用一些SQL函数,例如 LOWER 和 SUBSTR. 要使用任意SQL函数,请使用 fn() 要构造查询的对象:

# Select the user's id, username and the first letter of their username, lower-cased
first_letter = fn.LOWER(fn.SUBSTR(User.username, 1, 1))
query = User.select(User, first_letter.alias('first_letter'))

# Alternatively we could select only users whose username begins with 'a'
a_users = User.select().where(first_letter == 'a')

>>> for user in a_users:
...    print(user.username)

SQL帮助器

有时您可能希望简单地传递一些任意的SQL。你可以用特制的 SQL 班级。一个用例是在引用别名时:

# We'll query the user table and annotate it with a count of tweets for
# the given user
query = (User
         .select(User, fn.Count(Tweet.id).alias('ct'))
         .join(Tweet)
         .group_by(User))

# Now we will order by the count, which was aliased to "ct"
query = query.order_by(SQL('ct'))

# You could, of course, also write this as:
query = query.order_by(fn.COUNT(Tweet.id))

有两种方法可以使用peewee执行手工编制的SQL语句:

  1. Database.execute_sql() 用于执行任何类型的查询

  2. RawQuery 用于执行 SELECT 查询并返回模型实例。

安全性和SQL注入

默认情况下,peewee将参数化查询,因此用户传入的任何参数都将被转义。此规则的唯一例外是,如果编写原始SQL查询或传入 SQL 对象,它可能包含不受信任的数据。要减轻这种情况,请确保将任何用户定义的数据作为查询参数传入,而不是作为实际SQL查询的一部分传入:

# Bad! DO NOT DO THIS!
query = MyModel.raw('SELECT * FROM my_table WHERE data = %s' % (user_data,))

# Good. `user_data` will be treated as a parameter to the query.
query = MyModel.raw('SELECT * FROM my_table WHERE data = %s', user_data)

# Bad! DO NOT DO THIS!
query = MyModel.select().where(SQL('Some SQL expression %s' % user_data))

# Good. `user_data` will be treated as a parameter.
query = MyModel.select().where(SQL('Some SQL expression %s', user_data))

备注

mysql和postgresql的使用 '%s' 表示参数。另一方面,sqlite使用 '?' . 请确保使用适合您的数据库的字符。您也可以通过检查来找到这个参数 Database.param .