I'm new to Django but otherwise quite a seasoned python and sql
programmer. I'm baffled by django's foreign key id behaviour and as a
result stuck in my current coding project. Let me start immediately
with an example of what I'm trying to do. Assume two very simple
models:
class A(models.Model):
pass
class B(models.Model):
a = models.ForeignKey(A)
Now I need to create an instance of A and B without saving either of
them (I'm parsing a whack load of data and only want to commit objects
to the db once successfully parsed):
from test.models import A,B
instA = A()
instB = B(a=instA)
Now I'm done with my parsing and am happy to save them to my database:
instA.save() # all good
instB.save() # fails with IntegrityError: null value in column "a_id"
violates not-null constraint
On closer inspection the second line above fails because instB.a_id is
None, even though instB.a.id is defined. Why does django throw in a
duplicate, now stale instB.a_id field which, to make matters worse, is
used in generating the underlying SQL for instB.save() to result in
failure????
Perhaps I'm just to n00b to use the correct semantics which avoid the
above issues... excuse me if that's the case.
Gunther
def prepare_object(obj): # helper method to work around django
restrictions of NULL foreign keys before saving
for key in obj.__dict__:
if key[0] == '_' or key[-3:] != '_id': # skip private
and ordinary fields
continue
fkey_name = key[:-3]
try:
exec 'obj.%s = obj.%s.id' % (key, fkey_name)
except:
pass # there are some normal attributes that end
in _id but have got nothing to do with fkeys (hence don't have such
attributes)
Perhaps it will spare another lone programmer a hair or two. If nobody
has any further comments on this I think I'll just file a bug on
django's trac, this issue really *shouldn't* be present.
Gunther.
Well, just try to make sure you are passing already saved instance (having primary key set)
into related model which has foreign key to the first one.
Your approach:
> from test.models import A,B
> instA = A()
> instB = B(a=instA)
>
> Now I'm done with my parsing and am happy to save them to my database:
>
> instA.save() # all good
> instB.save() # fails with IntegrityError: null value in column "a_id"
> violates not-null constraint
Could easily be fixed:
> from test.models import A,B
> instA = A()
> #instB = B(a=instA) # We will do that later
>
> Now I'm done with my parsing and am happy to save them to my database:
>
> instA.save() # all good
> instB = B(a=instA) # Now is the time
> instB.save() # shouldn't raise any exception
This is common problem if you are trying to get use to ORM. I had many problems with
understanding Hibernate/JPA a while ago when coding in Java.
IntegrityError was risen because instA was cached at instB (you may check this by comparing instB.a.id and instB.__dict__), and changes in instA
haven't been pushed. It's common approach for Object-Relation Mappers.
It's probably tempting to prepare your objects first but preparing your data before
is even better. If there would be not too much confusion you can try to use dicts as data
storage first, then just pass it to the constructors of your models - just a note, I do
this quite frequently in some cases.
But if you are not convinced and you really have to prepare objects first you may simply
set instA to instB just before calling ``save`` method at instB. I would be:
> from test.models import A,B
> instA = A()
> instB = B() # prepare but don't set instA yet
>
> Now I'm done with my parsing and am happy to save them to my database:
>
> instA.save() # all good
> instB.a = instA # now we set instA, *after* it has proper pk already set
> instB.save() # shouldn't raise any exception
Hope it helps
> --
>
> You received this message because you are subscribed to the Google Groups "Django users" group.
> To post to this group, send email to django...@googlegroups.com.
> To unsubscribe from this group, send email to django-users...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/django-users?hl=en.
>
>