views:

223

answers:

2

How can I make a ForeignKey refer back to the object itself? I'm trying :

Alias(MyBaseModel):
    type = models.ForeignKey('self')

a = Alias()
a.type = a
a.save()

But then when I run it :

(1048, "Column 'type_id' cannot be null")

I don't want the type to be null, I want it to contain its own ID. I have tons of objects, but only 1 loops back to itself, so I really don't want tot make it null. Ideas?

+2  A: 

The problem is that when you did a.type = a, a did not yet exist in the database, therefore it has no pk.

One way around this is to save twice, first to save it into the database, referring to a dummy object that's already in the database, then save it again once you can get it from the database. One problem with this is there's sort of a chicken and egg problem of actually getting such an object into the database in the first place. I'd deal with this by means of creating a fixture to run each time you use sync-db, so that one such object exists and is valid.

Another option is to relax the not-null constraint and be diligent about getting them assigned.

Either way, inserting into such a table will always be a pain because you can't get the PK to use without first inserting, and you can't insert without having a key to use in the reference field, regardless of ORM or database or anything.

an alternative solution is to break the circular dependency and do something like:

AliasType(django.db.Model):
    pass

Alias(MyBaseModel):
    type = models.ForeignKey(AliasType)

a = Alias()
a_t = AliasType()
a_t.save()
a.type = a_t
a.save()

You can still get useful information by using django's very smart accessors,

AliasType.objects.all()[0].alias_set

which can get you back to the original Alias object without having an explicit link to it.

TokenMacGuy
Hmm, lots of options, sadly none of them look too good :( I went with the fixture. Created a file `fixtures/initial.json` which I figured out the format from `./manage.py dumpdata`. Thanks.
Paul Tarjan
+1  A: 

Shouldn't your type field be null=True, blank=True? Otherwise what is your base case? I mean if you only have one object in the db what is it's type?

Having the object loop onto itself can be overcome by adopting a convention that an empty type field means exactly that - that object refers to itself!

If type field is not empty, that means it refers to another Alias object, which works out just peachy.

Your question does not make sense at the algorithmic level (unless i am missing something), so how can you make it work in db?

Alias(MyBaseModel):
    type = models.ForeignKey('self', blank=True, null=True)

a = Alias()
a.save()
# Now a refers to itself

b = Alias()
b.type = a
b.save()
# b does not refer to itself, but rather a

I know I redefined the problem a little, so sorry if that's not what you are after! Cheers.

drozzy
Thanks for your input. If I change the blank case to mean myself, then I have to create a fetch method that returns the object itself when I try to render the type. The fixture solution seems to solve it a bit better.
Paul Tarjan