Opened 16 years ago
Closed 13 years ago
#13299 closed Bug (duplicate)
Handicapped objects passed to models signals during loaddata
| Reported by: | Patryk Zawadzki | Owned by: | nobody | 
|---|---|---|---|
| Component: | Documentation | Version: | 1.2-beta | 
| Severity: | Normal | Keywords: | |
| Cc: | Triage Stage: | Accepted | |
| Has patch: | no | Needs documentation: | no | 
| Needs tests: | no | Patch needs improvement: | no | 
| Easy pickings: | no | UI/UX: | no | 
Description
The documentation:
http://docs.djangoproject.com/en/1.1/ref/signals/#django.db.models.signals.post_save
...states that actual model instances are to be expected in post_save and pre_save signals.
This is not the case during loaddata. Models that use natural python inheritance (not proxy or abstract) are passed as handicapped instances that only have their own fields filled. This breaks signals that try to access any of the fields of the parent model. This happens because loaddata passes raw=True to the save method.
Consider the following:
class Foo(models.Model):
    foo_field = models.IntegerField(default=0)
class Bar(Foo):
    bar_field = models.IntegerField(default=0)
During loaddata the instance is passed as follows:
instance.id = None instance.foo_field = 0 instance.bar_field = 3
While...
instance.foo_ptr_id = 1 instance.foo_ptr.id = 1 instance.foo_ptr.foo_field = 2
I can think of three solutions:
- document it throughly and suggest doing something like:
if not instance.id: instance = instance.__class__.objects.get(id=instance.pk) 
- make save_basereconstruct the object before calling signals
- ignore signals at all during raw object saves
Change History (8)
comment:1 by , 16 years ago
comment:2 by , 16 years ago
| Component: | Uncategorized → Documentation | 
|---|---|
| Triage Stage: | Unreviewed → Accepted | 
The issue is that when you call loaddata, we are specifically saving only the attributes that are local to the model; if we don't do this, we get a bunch of extra objects created up the inheritance hierarchy. This is what the raw save does. As a consequence of this, we don't actually have access to the values for the parent fields at time of save.
I think you're right that this may need to be a documentation fix; doing the database lookup to populate the parent fields will be expensive for anyone that doesn't need it. 
comment:3 by , 16 years ago
Yes, I understand why parent object data is not available when loading a fixture and agree that doing an extra select for each insert is not a good idea.
Documenting raw=True would also let people skip signals during loading of fixtures. This might be the desired behavior if the fixture also contains objects normally created by post_save.
comment:4 by , 15 years ago
| Severity: | → Normal | 
|---|---|
| Type: | → Bug | 
comment:8 by , 13 years ago
| Resolution: | → duplicate | 
|---|---|
| Status: | new → closed | 
The documentation update is addressed in the patch for #20136.
Ok, after consulting current trunk I see that
rawkwarg is already being passed to the signal so I guess this is a documentation request.Maybe something like:
There are some rare cases (for example during fixture loading) when your
post_saveandpre_savesignal handlers receive a "raw" instance. This is signalled by passingTruefor therawkeyword argument. "Raw" instances do not contain any fields inherited from the parent model. If your application relies on model inheritance, make sure you take this into account. You can get a regular instance by using code below:if raw: instance = instance.__class__._default_manager.get(pk=instance.pk)