在省略一个或多个字段实例化的模型上调用
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。
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'));"
)