Django post_save signal behaves strangely with models using multi-table inheritance

Django post_save signal behaves strangely with models using multiple table inheritance

I notice strange behavior in the way the Django post_save signal works when using a model with overlapping multiple tables.

I have two models:

class Animal(models.Model): category = models.CharField(max_length=20) class Dog(Animal): color = models.CharField(max_length=10) 

I have a response to saving a message called echo_category:

 def echo_category(sender, **kwargs): print "category: '%s'" % kwargs['instance'].category post_save.connect(echo_category, sender=Dog) 

I have this device:

 [ { "pk": 1, "model": "animal.animal", "fields": { "category": "omnivore" } }, { "pk": 1, "model": "animal.dog", "fields": { "color": "brown" } } ] 

In each part of the program, except in the post_save callback, the following is true:

 from animal.models import Dog Dog.objects.get(pk=1).category == u'omnivore' # True 

When I run syncdb and install the device, the echo_category function is launched. Syncdb Output:

 $ python manage.py syncdb --noinput Installing json fixture 'initial_data' from '~/my_proj/animal/fixtures'. category: '' Installed 2 object(s) from 1 fixture(s) 

It is strange that the attribute of the category of dog objects is an empty string. Why is it not β€œomnivorous,” like everywhere else?

As a temporary (hopefully) workaround, I reload the object from the database in the post_save callback:

 def echo_category(sender, **kwargs): instance = kwargs['instance'] instance = sender.objects.get(pk=instance.pk) print "category: '%s'" % instance.category post_save.connect(echo_category, sender=Dog) 

This works, but it’s not what I like, because I have to keep this in mind when the model is inherited from another model, and it has to go back to the database. Another weird thing that I have to do instance.pk to get the primary key. The regular id attribute does not work (I cannot use instance.id). I do not know why this is so. Maybe this is due to why the category attribute is not working correctly?

+4
source share
1 answer

This is due to the fact that the data downloaded from the device using the loaddata / syncdb is saved as raw in the database: only the fields of the resulting model table are saved to avoid getting into the database for all models in the hierarchy class.

But, when the model is saved as raw, in your signal you get an additional argument to the raw keyword, so you can handle the case correctly. Your signal will be like this:

 def echo_category(sender, **kwargs): if kwargs.get('raw', False): instance = sender.objects.get(pk=kwargs['instance'].pk) else: instance = kwargs['instance'] print "category: '%s'" % instance.category post_save.connect(echo_category, sender=Dog) 

Thus, you will receive only an additional query to the database when working with devices (and I assume that this is acceptable in your case).

About another question:

Another strange fact: I have to do instance.pk to get the primary key. The regular id attribute does not work (I cannot use instance.id). I do not know why this is so.

id and pk have slightly different semantics. In your example, the id object of the Dog object is AutoField defined ( automatically) in your Animal class. However, pk is OneToOneField (once again automatically detected ) in the Dog class.

In practice, both fields have the same value all the time. However, since id is a field coming from Animal , it will not exist for a Dog object stored as raw.

Hope this helps.

EDIT : This issue has already been reported about django trac here .

+5
source

Source: https://habr.com/ru/post/1306436/


All Articles