1

I'm trying to extend a library model through multi-table inheritance and this is what I ended up with:

class CompetitionMedia(InstagramMedia):
    visible = models.BooleanField(default=True)
    most_creative = models.BooleanField(default=False)

@receiver(post_save, sender=InstagramMedia, dispatch_uid='create_competition_media')
def create_competition_media(sender, instance, created, **kwargs):
    competition_media = CompetitionMedia()
    competition_media.instagrammedia = instance
    competition_media.save() # fails

@receiver(post_save, sender=InstagramMedia, dispatch_uid='create_competition_media2')
def create_competition_media2(sender, instance, created, **kwargs):
    CompetitionMedia.objects.create(instagrammedia=instance) # Fails

Is it possible to do this?

defuz
  • 26,721
  • 10
  • 38
  • 60
Kit Sunde
  • 35,972
  • 25
  • 125
  • 179
  • You need to save only when new instance is created. What error do you get btw? – Rohan Sep 10 '12 at 05:59
  • @Rohan I excluded it when I was simplifying the examples, but you are correct. The errors I'm getting is that the field `instagrammedia` doesn't exist. – Kit Sunde Sep 10 '12 at 17:04

2 Answers2

1

As I understand it, you want to get something like this:

class CompetitionMedia(models.Model):
    instagrammedia = models.OneToOneField(InstagramMedia, primary_key=True)
    visible = models.BooleanField(default=True)
    most_creative = models.BooleanField(default=False)

@receiver(post_save, sender=InstagramMedia, dispatch_uid='create_competition_media2')
def create_competition_media(sender, instance, created, **kwargs):
    if created:  
         CompetitionMedia.objects.create(instagrammedia=instance)
defuz
  • 26,721
  • 10
  • 38
  • 60
  • While this is one way to accomplish the same goal, I actually only wanted to know if it was possible with multi-table inheritance rather than composition. – Kit Sunde Sep 10 '12 at 17:03
  • This is not quite what you want, but maybe it would be an acceptable solution (an example from my project): https://gist.github.com/3693618 – defuz Sep 10 '12 at 20:29
  • But as far as I know, usually what you want is impossible. Inheritance will create a separate table, or does nothing if it is `proxy=True` or `abstract=True` model. The obvious way - override metaclass of models. – defuz Sep 10 '12 at 20:38
0

I'm apparently supposed to set parent_ptr, like:

@receiver(post_save, sender=InstagramMedia, dispatch_uid='create_competition_media')
def create_competition_media2(sender, instance, created, **kwargs):
    if created:
        CompetitionMedia.objects.create(instagrammedia_ptr=instance)

However there's an issue where if you create and save the parent first and then try to create a child, the child will override te parent even on fields you haven't set in the child. As described in https://code.djangoproject.com/ticket/11618 and https://code.djangoproject.com/ticket/7623, so I would avoid it just because it's not very obvious that would happen.

If someone really wants to go down that route you'd need to do:

@receiver(post_save, sender=InstagramMedia, dispatch_uid='create_competition_media')
def create_competition_media2(sender, instance, created, **kwargs):
    if created:
        CompetitionMedia.objects.create(instagrammedia_ptr=instance)
        instance.save() # This should re-save the parent values.

better yet:

@receiver(post_save, sender=InstagramMedia, dispatch_uid='create_competition_media')
def create_competition_media2(sender, instance, created, **kwargs):
    if created:
        CompetitionMedia.objects.create(instagrammedia_ptr=instance,
            **instance.__dict__)
Kit Sunde
  • 35,972
  • 25
  • 125
  • 179
  • When you are creating derived class instance with existing base class instance, you need to copy base's attributes in derived as well. This question is helpful for that http://stackoverflow.com/questions/4064808/django-model-inheritance-create-sub-instance-of-existing-instance-downcast – Rohan Sep 11 '12 at 03:55
  • @Rohan I like that answer, it's cleaner (although still *weird*) than re-saving the parent and has 1 db hit less. – Kit Sunde Sep 11 '12 at 11:12
  • Remove the PK from the dictionary or use `force_insert` (though the latter one probably interferes with the pk sequence). See https://docs.djangoproject.com/en/1.6/ref/models/querysets/#create – Risadinha Sep 25 '14 at 10:00