【问题标题】:possible bug in django modelsdjango 模型中可能存在的错误
【发布时间】:2011-04-17 20:55:43
【问题描述】:

models.py:

class root(models.Model):
      uid_string = models.CharField(max_length=255, unique=True) 

class tree(models.Model):
      uid_string = models.ForeignKey(root, to_field='uid_string', db_column='uid_string')

class shrub(models.Model):
      uid_string = models.ForeignKey(root, to_field='uid_string')

显然,shrub 中的列将是 uid_string_id,而 tree 中的列将是 uid_string。 _id 附录被抑制。

如果我现在做一个

rootentry = root(uid_string = "text")
root.save()

我在执行以下查询时得到不同的行为:

>>> shrubentry = shrub(uid_string_id = rootentry.uid_string)
>>> treeentry = tree(uid_string = rootentry.uid_string)                                                             
Traceback (most recent call last):                                                                                  
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python2.6/site-packages/django/db/models/base.py", line 328, in __init__
    setattr(self, field.name, rel_obj)
  File "/usr/local/lib/python2.6/site-packages/django/db/models/fields/related.py", line 318, in __set__
    self.field.name, self.field.rel.to._meta.object_name))
ValueError: Cannot assign "'text'": "tree.uid_string" must be a "root" instance.
>>> 

显然rootentry.uid_stringtext

【问题讨论】:

    标签: django django-models models


    【解决方案1】:

    Django 的行为与预期的一样。为了理解为什么我们将首先看看在外键关系的情况下将关键字参数传递给模型类的构造函数的方法。

    1. 使用关系名称 (uid_string)。在这种情况下,您必须传递相关模型的 instance (uid_string = rootentry)。

    2. 使用数据库字段的名称 (uid_string_id)。在这里,您必须传递一个适当的 type 值。所以如果 FK 指向一个整数字段,则传递一个整数;如果它指向文本,则传递文本实例等。

    现在让我们看看您的代码。我们将从第一行开始:

    shrubentry = shrub(uid_string_id = rootentry.uid_string)
    shrubentry.save() # Succeeds
    

    您已在 shrubroot 之间创建了关系,但还指定了要链接到的自定义 to_field。由于此列是一个文本字段,您可以将rootentry.uid_string 作为uid_string_id 关键字参数的值传递(上面列出的机制#2)。

    还有另一种方式来表达您在上面所做的事情,而不使用字段名称作为关键字参数。那将是使用关系的名称并传递一个根实例(上面列出的机制#1)。

    shrubentry = shrub(uid_string = rootentry)
    shrubentry.save() # Succeeds
    

    现在让我们看看第二行

    treeentry = tree(uid_string = rootentry.uid_string) # Raises error.
    # ValueError: Cannot assign "'text'": "tree.uid_string" must be a "root" instance.
    

    这一行与第一行不同。您正在使用机制 #1(关系名称),因此 Django 期望 root 的实例作为关键字参数的值。如果切换到机制 #2(字段名称):

    treeentry = tree(uid_string_id = rootentry.uid_string)
    treeentry.save() # Succeeds
    

    现在是有趣的部分。试试这个:

    treeentry = tree(uid_string_id = rootentry.id)  # No problem
    treeentry.save() # Blows up
    # IntegrityError: ...
    # DETAIL:  Key (uid_string)=(2) is not present in table "app_root".
    

    上述sn -p的第一行有效。但是当您尝试保存它时,数据库会在root 表的_uid_string_ 列中查找键“2”(即rootentry.id)。由于它不存在,save() 失败。

    【讨论】:

    • 感谢您的回答。据我了解,您使用 to_field='uid_string' 指定关系,并通过 db_column="" 简单地指定列的名称。省略 to_field 将引用主键 (id)。我错了吗?
    • 你说得对。省略to_field 是正确的。我的错。我正在编辑我的答案。
    • 感谢您的努力。但请再看一遍。两种模型都有一个to_field 来指定列。除了由db_column定义的列名之外,模型是相同的。
    • @tpm:上面已经更新了答案。添加了save() 电话以更好地说明我的观点。看看有没有帮助。
    • 再次感谢。你的 cmets 真的让我更好地理解了事情。我在 testapp 中尝试了你的例子,是的,它就像你展示的那样工作。但是:只有一件事让我感到困惑:使用我的第一个 sn-p 中显示的模型,数据库中的表 tree 没有名为 uid_string_id 的列,而只是 uid_string(而 shrub 表有一个名为uid_string_id 的列)。尽管如此,treeentry = tree(uid_string_id = rootentry.uid_string) 仍然有效,尽管在 tree 中没有名为 uid_string_id 的列。 django 是否以特殊方式处理_id 后缀?
    【解决方案2】:

    在处理外键时,您需要使用如下的对象实例。

    treeentry = tree(uid_string = rootentry)
    

    顺便说一句,类名使用 CamelCase。请阅读http://www.python.org/dev/peps/pep-0008/

    【讨论】:

      猜你喜欢
      • 2016-08-16
      • 2012-12-18
      • 2021-10-08
      • 2015-04-04
      • 1970-01-01
      • 1970-01-01
      • 2019-04-18
      • 2013-10-11
      • 2019-12-22
      相关资源
      最近更新 更多