开发者

possible bug in django models

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

obviously, the column in shrub will be uid_string_id while the column in tree will bee uid_string. the _id appendix is supressed.

If I now do a

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

I get different behaviour doing the following queries:

>>> 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 <modul开发者_如何学运维e>
  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.
>>> 

obviously rootentry.uid_string is text


Django is behaving just as expected. To understand why we'll start with a look at the ways to pass keyword arguments to a model class' constructor in case of a foreign key relationship.

  1. Use the name of the relationship (uid_string). In this case, you have to pass an instance of the related model (uid_string = rootentry).

  2. Use the name of the database field (uid_string_id). Here you have to pass a value of an appropriate type. So if the FK points to an integer field, pass an integer; if it points to text, pass a text instance etc.

Now let us look at your code. We'll start with the first line:

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

You have created a relation between shrub and root but have also specified a custom to_field to link to. Since this column is a text field you are able to pass the rootentry.uid_string as the value of the uid_string_id keyword argument (mechanism #2 listed above).

There is another way to express what you did above, without using the field name as the keyword argument. That will be to use the name of the relationship and pass an instance of root (mechanism #1 listed above).

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

Now let us take a look at the second line.

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

This line is different from the first line. You are using mechanism #1 (relationship name) and therefore Django expects an instance of root as the value of the keyword argument. If you switch to mechanism #2 (field name):

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

Now comes the interesting part. Try this:

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

The first line of the above snippet works. But when you try to save it the database looks for a key "2" (i.e. rootentry.id) in the _uid_string_ column of the root table. Since it is not there, the save() fails.


When dealing with foreignkeys, you need to use object instance like below.

treeentry = tree(uid_string = rootentry)

BTW, use CamelCase for Class names. Please read http://www.python.org/dev/peps/pep-0008/

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜