Peewee 在严格模式下默默地将不带默认值的非空列的空值插入 MySQL

问题描述 投票: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 的

MySQLDatabase
类及其
MySQLConnectorDatabase
子类配置为将
sql_mode
设置为
PIPES_AS_CONCAT
,从而定义始终覆盖 MySQL 全局模式的会话模式。

正如@coleifer所提到的,打开与MySQL的连接时需要手动设置

sql_mode
以抑制覆盖:

debug_db = MySQLDatabase(
    database = 'debug_db',
    user = 'DEBUG',
    host = 'localhost',
    password = 'secret',
    <strong>sql_mode = 'TRADITIONAL'</strong>
)

具体来说,Peewee 会覆盖

sql_mode
连接参数,在 PyMySQL 和 MySQL Connector/Python 中其默认值为
None
,以设置模式
PIPIES_AS_CONCAT
:

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

要保留

PIPES_AS_CONCAT
而不覆盖全局
sql_mode
,可以使用
sql_mode
连接参数将其附加到
init_command
,同时中和 Peewee 的
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.