Foo.objects.get(id = None)有时会返回Foo实例

aeh*_*lke 9 python mysql django innodb django-models

我有这个代码:

try:
    parent_comment = models.Comment.all_objects.get(id=parent_comment_id)
except models.Comment.DoesNotExist:
    parent_comment = None

if parent_comment is not None and parent_comment_id is None:
    raise Exception("WTF django/mysql")
Run Code Online (Sandbox Code Playgroud)

......有时,异常会以某种方式被提出.怎么会发生这种情况?

偶尔,一天几次,它返回看似随机的评论实例.通常它的行为与预期一致,并返回None.

这是Comment表的id字段:id int(11) NOT NULL AUTO_INCREMENT所以它不是可以为空的.这是一个InnoDB表.

对于Comment.all_objects,这是它​​的定义:all_objects = Manager()它是该类中的第一行.

我们在Django 1.2.7上.

更新 添加了对异常的日志记录,以获取引发异常时生成的SQL.这里是:

SELECT `canvas_comment`.`id`, `canvas_comment`.`visibility`, `canvas_comment`.`parent_content_id`, `canvas_comment`.`parent_comment_id`, `canvas_comment`.`timestamp`, `canvas_comment`.`reply_content_id`, `canvas_comment`.`reply_text`, `canvas_comment`.`replied_comment_id`, `canvas_comment`.`category_id`, `canvas_comment`.`author_id`, `canvas_comment`.`title`, `canvas_comment`.`ip`, `canvas_comment`.`anonymous`, `canvas_comment`.`score`, `canvas_comment`.`judged`, `canvas_comment`.`ot_hidden` FROM `canvas_comment` WHERE `canvas_comment`.`id` IS NULL
Run Code Online (Sandbox Code Playgroud)

sup*_*cuo 13

这种行为是由非常奇怪的(在这个编码器的拙见)MySQL行为引起的,SQL_AUTO_IS_NULL变量控制(1默认情况下在MySQL <5.5.3中):

如果此变量设置为1,则在成功插入自动生成的AUTO_INCREMENT值的语句之后,您可以通过发出以下格式的语句来查找该值:

SELECT * FROM tbl_name WHERE auto_col IS NULL
Run Code Online (Sandbox Code Playgroud)

如果语句返回一行,则返回的值与调用LAST_INSERT_ID()函数时的值相同

一个Django错误(关闭:wontfix)描述了由这个"功能"引起的类似混淆,其中一个核心开发者说明

如果您不想要这种行为,则应该配置数据库以根据您的偏好做正确的事情

然后,解决方案是使用该语句禁用SQL_AUTO_IS_NULLMySQL数据库的选项.您可以使用以下内容执行此操作:SETsettings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        # ... your DB options
        'OPTIONS': {
            'init_command': 'SET SQL_AUTO_IS_NULL=0;'
        },
    }
}
Run Code Online (Sandbox Code Playgroud)

从长远来看,你可以尝试在django-developers列表上敲击,让他们重新考虑他们之前的位置:

幸运的是,我在这里的想法并没有烧掉任何桥梁.如果有人想要测试它,或者默认使用它,他们可以在设置中通过DATABASE_OPTIONS使用数据库初始化选项......"read_default_file"和"init_command"都在那里很有用.

我不是说这应该是"不,永远",但是现在我不相信它值得做到这一点.与之平衡的是如何让人们知道它可能会发生...... 耸耸肩 ......我可能会向databases.txt添加一些东西,作为开始.我讨厌这种平衡行为.:-(