views:

65

answers:

2

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

obviously rootentry.uid_string is text

A: 

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/

Srikanth Chundi
+2  A: 

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.

Manoj Govindan
Thank you for your answer. As far as I understood it, you specify the relationship using to_field='uid_string' and simply the NAME of the column by db_column="" . Leaving out the to_field would reference to the primary key (id). Am I wrong?
tpm
You have it right. Leaving out the `to_field` is correct. My mistake. I'm editing my answer.
Manoj Govindan
Thank you for your effort. But please take a look again. Both models have a `to_field` to specify the column. The models are the same except for the column name defined by `db_column`.
tpm
@tpm: have updated answer above. Added `save()` calls to better illustrate my point. See if it helps.
Manoj Govindan
thanks again. Your comments really made me understand things better. I tried your examples in a testapp and yes, it just works as you show it. BUT: there's only one thing that confuses me: using the models shown in my first snippet, the table `tree` in the database has no column named `uid_string_id` but simply `uid_string` (whilst the `shrub` table has a column named `uid_string_id`). Nevertheless, `treeentry = tree(uid_string_id = rootentry.uid_string)` works, although there's no column named `uid_string_id` in `tree`. Is django processing the `_id` suffix in a special way?
tpm