Opened 14 years ago

Closed 11 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:

  1. document it throughly and suggest doing something like:
    if not instance.id: instance = instance.__class__.objects.get(id=instance.pk)
    
  1. make save_base reconstruct the object before calling signals
  1. ignore signals at all during raw object saves

Change History (8)

comment:1 by Patryk Zawadzki, 14 years ago

Ok, after consulting current trunk I see that raw kwarg 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_save and pre_save signal handlers receive a "raw" instance. This is signalled by passing True for the raw keyword 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)

comment:2 by Russell Keith-Magee, 14 years ago

Component: UncategorizedDocumentation
Triage Stage: UnreviewedAccepted

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 Patryk Zawadzki, 14 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 Julien Phalip, 13 years ago

Severity: Normal
Type: Bug

comment:5 by Aymeric Augustin, 12 years ago

UI/UX: unset

Change UI/UX from NULL to False.

comment:6 by Aymeric Augustin, 12 years ago

Easy pickings: unset

Change Easy pickings from NULL to False.

comment:7 by Julien Phalip, 12 years ago

See also #8399 which suggests adding a new pre_loaddata signal.

comment:8 by Tim Graham, 11 years ago

Resolution: duplicate
Status: newclosed

The documentation update is addressed in the patch for #20136.

Note: See TracTickets for help on using tickets.
Back to Top