Paul Tarjan
Paul Tarjan

Reputation: 50642

Not null ForeignKey('self')

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?

Upvotes: 1

Views: 2585

Answers (3)

Rajan Mandanka
Rajan Mandanka

Reputation: 2053

set null=true , blank=true in your code
like,

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

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

Upvotes: 0

Andriy Drozdyuk
Andriy Drozdyuk

Reputation: 61111

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.

Upvotes: 1

SingleNegationElimination
SingleNegationElimination

Reputation: 156238

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.

Upvotes: 4

Related Questions