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.
Use the name of the relationship (
uid_string
). In this case, you have to pass an instance of the related model (uid_string = rootentry
).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/
精彩评论