Peewee 覆盖默认 SQL 模式

问题描述 投票:0回答:1

在省略一个或多个字段实例化的模型上调用

save()
create()
insert().execute()
时不会引发错误,即使在配置为
null=False
default=None
的字段(所有字段的默认设置) )尽管 MySQL 被配置为全局使用严格模式:

mysql> SET GLOBAL sql_mode="TRADITIONAL";
Query OK, 0 rows affected (0.00 sec)
from rich import inspect

from peewee import Model, MySQLDatabase
from peewee import CharField, FixedCharField, BooleanField, DateTimeField

debug_db = MySQLDatabase(
    database = 'debug_db',
    user = 'DEBUG',
    host = 'localhost',
    password = 'secret'
)

class Person(Model):    
    first_name = CharField(32)
    last_name = CharField(32, null=False)
    email = FixedCharField(255)
    signup_time = DateTimeField()
    approved = BooleanField()
    
    class Meta:
        database = debug_db

debug_db.connect()
debug_db.create_tables([Person])

john_doe = Person(
    first_name = "John"
)

inspect(john_doe)

# │     approved = None                             │
# │ dirty_fields = [<CharField: Person.first_name>] │
# │        email = None                             │
# │   first_name = 'John'                           │
# │           id = None                             │
# │    last_name = None                             │
# │  signup_time = None                             │

john_doe.save()

# mysql> select * from person;
# +----+------------+-----------+-------+---------------------+----------+
# | id | first_name | last_name | email | signup_time         | approved |
# +----+------------+-----------+-------+---------------------+----------+
# |  1 | John       |           |       | 0000-00-00 00:00:00 |        0 |
# +----+------------+-----------+-------+---------------------+----------+
# 1 row in set (0.00 sec)

# Debug logger:
# ('SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE() AND table_type != %s ORDER BY table_name', ('VIEW',))
# ('INSERT INTO `person` (`first_name`) VALUES (%s)', ['John'])

在严格模式下,直接向 MySQL 发出等效的

INSERT
语句会引发错误:

# mysql> INSERT INTO person (first_name) VALUES ("John");
# ERROR 1364 (HY000): Field 'last_name' doesn't have a default value

如示例所示,检查实例会发现省略的属性在内部设置为

None
。有趣的是,手动执行此操作会在 Peewee 中触发错误,并在生成的 SQL 中包含
None
值字段:

john_doe = Person(
    first_name = "John",
    last_name = None
)

# peewee.IntegrityError: (1048, "Column 'last_name' cannot be null")

# Debug logger:
# ('INSERT INTO `person` (`first_name`, `last_name`) VALUES (%s, %s)', ['John', None])

在 Peewee 的文档中,有关查询的章节包括一个示例,逐渐为最初省略某些字段创建的对象赋值,因此在实例化时允许省略必须是有意的,但我预计在行被删除之前的某个时刻会出现错误插入,或者由生成的 SQL 语句产生,或者在调用

save()
时产生。

相比之下,使用 SQLite 而不是 MySQL 会在 Peewee 中触发错误:

peewee.IntegrityError: NOT NULL constraint failed: person.last_name

我还使用

playhouse.mysql_ext.MySQLConnectorDatabase
进行了测试,它产生与默认 MySQL 驱动程序相同的结果。

我使用的是 Peewee 3.17、MySQL 8.0.31 和 Python 3.10.5。

python mysql peewee
1个回答
0
投票

Peewee 默认设置

sql_mode
连接参数,覆盖任何全局 SQL 模式(包括 MySQL 的默认 SQL 模式),因此正如 coleifer 指出的,在建立连接时需要手动设置
sql_mode
以阻止不完整的插入使用严格模式:

debug_db = MySQLDatabase(
    # ...
    sql_mode = 'TRADITIONAL' # One of 3 strict modes; not the MySQL default.
)

具体来说,Peewee 会覆盖

sql_mode
及其
None
子类中的
MySQLDatabase
连接参数(否则 PyMySQL 和 MySQL Connector/Python 中的
MySQLConnectorDatabase
),以设置 SQL 模式
PIPES_AS_CONCAT

#line 4147 of peewee.py (within the MySQLDatabase class definition)
sql_mode = "PIPES_AS_CONCAT"

要保留

PIPES_AS_CONCAT
而不覆盖全局 SQL 模式,可以在重置 Peewee 的
init_command
覆盖时使用
sql_mode
连接参数来附加它:

debug_db = MySQLDatabase(
    # ...
    sql_mode = None,
    init_command = "SET sql_mode=(SELECT CONCAT(@@sql_mode,',PIPES_AS_CONCAT'));"
)
© www.soinside.com 2019 - 2024. All rights reserved.