【问题标题】:Foo.objects.get(id=None) returns Foo instance, sometimesFoo.objects.get(id=None) 有时返回 Foo 实例
【发布时间】:2012-08-07 19:57:05
【问题描述】:

我有这个代码:

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")

...有时,异常会以某种方式引发。这怎么可能发生?

偶尔,一天几次,它返回看似随机的 Comment 实例。通常它的行为符合预期并返回 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

【问题讨论】:

  • 第 5 行的 parent_comment_id 是在哪里定义的?我在你的代码中找不到它。
  • @sza 这无关紧要 - 我问的是如何引发异常,当 parent_comment_id 为 None 时会发生这种情况。
  • 好的。但是如果你之前没有定义它或者给它赋值,它肯定是None。您可以粘贴与 parent_comment_id 分配相关的代码吗?谢谢!
  • @sza python 不能那样工作 - 如果它之前没有定义过,它会引发 NameError 异常。
  • @sza 因为其他原因不能。

标签: python mysql django django-models innodb


【解决方案1】:

这种行为是由非常奇怪的(在此编码员的拙见中)MySQL 行为引起的,受控by the SQL_AUTO_IS_NULL variable(在 MySQL 1):

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

SELECT * FROM tbl_name WHERE auto_col IS NULL

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

a Django bug (closed: wontfix) describing similar confusion是由这个“特性”引起的,其中一位核心开发者表示

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

因此,解决方案是禁用 MySQL 数据库 using the SET statementSQL_AUTO_IS_NULL 选项。您可以在settings.py 中执行此操作,例如:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        # ... your DB options
        'OPTIONS': {
            'init_command': 'SET SQL_AUTO_IS_NULL=0;'
        },
    }
}

从长远来看,您可以尝试在the django-developers list 上打鼓,让他们重新考虑their earlier position

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

我并不是说这应该是“不,永远”,但现在我不是 确信这样做是值得的。与之相平衡的是如何让 人们知道它可能会发生...耸耸肩..我可能会添加一些东西 databases.txt,首先。我讨厌这种平衡行为。 :-(

【讨论】:

    【解决方案2】:

    排除简单的东西:

    1. 能否发布您的评论模型
    2. 能否对您的数据库运行以下查询

    SELECT COUNT(id) FROM <your_comment_table> WHERE id <= 0 OR id ='' OR id IS NULL

    您的代码在逻辑上应该可以正常工作,除非您的模型代码中有一些不可靠的地方,这让我相信它一定是与数据相关的。

    编辑

    另一个想法,看看 django 的 ORM 正在查询什么(这应该告诉我们发生了什么):

    parent_comment = models.Comment.all_objects.get(id=parent_comment_id)
    raise Exception(parent_comment.query) # should display out the query thats being generated
    

    【讨论】:

    • 您的查询返回 0 行。将生成的 SQL 添加到问题中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-06
    • 2020-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多