I have the following model :
class A:
b = ForeignKey('B')
class B:
a = ForeignKey('A', null=True)
I have several methods that decode a json object and create a tree of objects A and B. Suppose I have an object of type A that I want to save, I'm trying to write something like this
in class A :
def save(self, *args, **kwargs):
self.b.save()
super(A, self).save(*args, **kwargs)
in B :
def save(self, *args, **kwargs):
if self.a:
self.a.save()
super(B, self).save(*args, **kwargs)
This does not work, even though A.b and B.a get assigned an id while calling save(), it still violates the not null constraint in A. I've read somewhere that this was due to how ORM works, and how the objects are cached somehow.
The solution pro开发者_开发百科posed was to do something like this :
a = A()
b = B()
b.save()
a.b = b
a.save()
But for obvious reason of recursion, it's not appropriate in my case. So the only work around I can think of is to provide each objects with a method that recursively retrieve every object that need to be saved, and then do a for loop to save each one in the right order. I really would like to avoid this, since of course the actual model is more complex and involves more than two classes and more than one Foreign Key per class.
So I guess my question is simply : Is there a better way or a more common way to proceed in such situations ?
Well after some soul searching I found out that this was indeed a cache "problem". I don't have the time to look inside django code to understand how things really work, so if anyone have some interesting insights that would be great.
In the meantime, the solution I found is to force the object to clear its cache like this :
def save(self, *args, **kwargs):
self.b.save()
self.b = self.b # yes ...
super(A, self).save(*args, **kwargs)
This does work, but for the record here is a little helper to automatically clear cache before any saving :
def clear_cache(obj):
map(lambda x: obj.__setattr__(x, obj.__getattribute__(x)), \ # force reassignment
map(lambda x: re.match('_(.*)_cache', x).groups()[0], \ # get attribute name
filter(lambda x: x.find('_cache') != -1, dir(obj)))) # get all attribute with cache
@receiver(pre_save)
def cache_callback(sender, instance, **kwargs):
clear_cache(instance)
It's worth noting that you're doing the save of the other objects before you call the superclass save method, which is probably the opposite of what you want.
Alternatively, and this might end up being cleaner, use post-save signals.
精彩评论