Multi-table Inheritance: How to add child to parent model?

2,437 views
Skip to first unread message

ringemup

unread,
Nov 4, 2010, 4:25:25 PM11/4/10
to Django users
I have an existing model that I want to extend using multi-table
inheritance. I need to create a child instance for each parent
instance in the database, but I can't figure out how. I've scoured
google and haven't come up with anything other than Ticket #7623[1].
Here are some of the things I've tried...

Let's adapt the Place / Restaurant example from the docs:

class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)

class Restaurant(Place):
place = models.OneToOneField(Place, parent_link=True,
related_name='restaurant')
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()

I want to do the following, in essence:

for place in Place.objects.all():
restaurant = Restaurant(**{
'place': place,
'serves_hot_dogs': False,
'serves_pizza': True,
})
restaurant.save()

Of course, doing this tries to also create a new Place belonging to
the new Restaurant, and throws an error because no values have been
specified for the name and address fields. I've also tried:

for place in Place.objects.all():
restaurant = Restaurant(**{
'serves_hot_dogs': False,
'serves_pizza': True,
})
place.restaurant = restaurant
place.save()

This, however, doesn't create any records in the restaurant table.

Any suggestions?

[1] http://code.djangoproject.com/ticket/7623

derek

unread,
Nov 5, 2010, 9:42:20 AM11/5/10
to Django users
Its not clear exactly what you think the problem is. The system is
behaving correctly ito the way you have set it up.

1. You have specified that name and address are compulsory fields in
Place.
2. You have specifed that Restaurant must be linked to Place (i.e.
Place must be created before Restaurant can be created)

So why would you now want an entry in Restaurant that does _not_ have
a name and address?

ringemup

unread,
Nov 5, 2010, 9:59:34 AM11/5/10
to Django users
I'm not trying to create a Restaurant without a name and address. I'm
trying to turn an existing Place into a Restaurant.

Derek

unread,
Nov 5, 2010, 10:25:25 AM11/5/10
to django-users
You mean "trying to add a Restaurant which is linked to an existing place"

I am not familar with the syntax you are using for the **{} wrapper.

The original example shows:
r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
and the attached note says:
Pass the ID of the "parent" object as this object's ID.
Are you sure that the place ID is, in fact, what is being transferred?


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.


ringemup

unread,
Nov 5, 2010, 12:24:34 PM11/5/10
to Django users
**{} is sort of the inverse of **kwargs in a function signature --
it's a way to use a dictionary in place of keyword arguments, and
should function identically.

Which "original example" are you referring to?



On Nov 5, 10:25 am, Derek <gamesb...@gmail.com> wrote:
> You mean "trying to add a Restaurant which is linked to an existing place"
>
> I am not familar with the syntax you are using for the **{} wrapper.
>
> The original example shows:
>
> r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
>
> and the attached note says:
>
> Pass the ID of the "parent" object as this object's ID.
>
> Are you sure that the place ID is, in fact, what is being transferred?
>
> > django-users...@googlegroups.com<django-users%2Bunsu...@googlegroups.com>
> > .

Derek

unread,
Nov 6, 2010, 5:59:42 AM11/6/10
to django...@googlegroups.com
The example shown in the online documentation.


To unsubscribe from this group, send email to django-users...@googlegroups.com.

ringemup

unread,
Nov 6, 2010, 8:47:36 AM11/6/10
to Django users
I can't find that example in this documentation [1]. Is there other
documentation that I'm not aware of?

[1] http://docs.djangoproject.com/en/dev/topics/db/models/#multi-table-inheritance


On Nov 6, 5:59 am, Derek <gamesb...@gmail.com> wrote:
> The example shown in the online documentation.
>
> > <django-users%2Bunsu...@googlegroups.com<django-users%252Buns...@googlegroups.com>

Derek

unread,
Nov 6, 2010, 3:47:08 PM11/6/10
to django...@googlegroups.com
See:
To unsubscribe from this group, send email to django-users...@googlegroups.com.

ringemup

unread,
Nov 6, 2010, 7:48:28 PM11/6/10
to Django users

That documentation is for pre-1.0 versions of Django, when model
inheritance wasn't available at all. Multi-table inheritance doesn't
seem to work identically to plain one-to-one relations.
> On 6 November 2010 14:47, ringemup <ringe...@gmail.com> wrote:
>
> > I can't find that example in this documentation [1].  Is there other
> > documentation that I'm not aware of?
>
> > [1]
> >http://docs.djangoproject.com/en/dev/topics/db/models/#multi-table-in...
> > > > <django-users%2Bunsu...@googlegroups.com<django-users%252Buns...@googlegroups.com>
> > <django-users%252Buns...@googlegroups.com<django-users%25252Bun...@googlegroups.com>

Michael Sprayberry

unread,
Nov 6, 2010, 10:02:10 PM11/6/10
to django...@googlegroups.com
Hello,
 
I was just going through this posting and I noticed that you are using concrete inheritance in your calling of
 
class Place(models.Model):
                   name = models.CharField(max_length=50)
                   address = models.CharField(max_length=80)
 
class Restaurant(Place):
                   place = models.OneToOneField(Place, parent_link=True,
                   related_name='restaurant')
                   serves_hot_dogs = models.BooleanField()
                   serves_pizza = models.BooleanField()
 
Try
 
class Place(models.Model):
                   name = models.CharField(max_length=50)
                   address = models.CharField(max_length=80)
 
                   class Meta:
                            abstract = True
 
class Restaurant(Place):
                   place = models.OneToOneField(Place, parent_link=True,
                   related_name='restaurant')
                   serves_hot_dogs = models.BooleanField()
                   serves_pizza = models.BooleanField()
 
 
This is abstract inheritance and any model inheriting Place will get a copy of Place's fields onto the child object instead of getting another Table placed there.
 
Michael 


--- On Sat, 11/6/10, ringemup <ring...@gmail.com> wrote:
> > > > > > django-users+unsub...@googlegroups.com<django-users%2Bunsu...@googlegroups.com>

> > <django-users%2Bunsu...@googlegroups.com<django-users%252Buns...@googlegroups.com>
>
> > > > <django-users%2Bunsu...@googlegroups.com<django-users%252Buns...@googlegroups.com>
> > <django-users%252Buns...@googlegroups.com<django-users%25252Bun...@googlegroups.com>
>
> > > > > > .
> > > > > > For more options, visit this group at
> > > > > >http://groups.google.com/group/django-users?hl=en.
>
> > > > --
> > > > You received this message because you are subscribed to the Google
> > Groups
> > > > "Django users" group.
> > > > To post to this group, send email to django...@googlegroups.com.
> > > > To unsubscribe from this group, send email to
> > > > django-users+unsub...@googlegroups.com<django-users%2Bunsu...@googlegroups.com>

> > <django-users%2Bunsu...@googlegroups.com<django-users%252Buns...@googlegroups.com>
>
> > > > .
> > > > For more options, visit this group at
> > > >http://groups.google.com/group/django-users?hl=en.
>
> > --
> > You received this message because you are subscribed to the Google Groups
> > "Django users" group.
> > To post to this group, send email to django...@googlegroups.com.
> > To unsubscribe from this group, send email to

> > .
> > For more options, visit this group at
> >http://groups.google.com/group/django-users?hl=en.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users+unsub...@googlegroups.com.

Stefan Foulis

unread,
Nov 7, 2010, 9:03:05 AM11/7/10
to Django users
As far as I know there is no existing api to convert a 'Place' into a
'Restaurant' with multi-table inheritance in django. There is a ticket
about this [1]. And in another ticket [2] that is marked as a
duplicate but seems to have an example of how to solve this problem
until there is a standard api for the operation. Translated to the
Restaurant example this would be:

restaurant = Restaurant(place_ptr = place)
for f in place._meta.local_fields: setattr(restaurant, f.name,
getattr(place, f.name))
restaurant.save()

[1] http://code.djangoproject.com/ticket/7623
[2] http://code.djangoproject.com/ticket/11618

ringemup

unread,
Nov 7, 2010, 10:15:29 AM11/7/10
to Django users
Thank you, Michael, but I need to use multi-table rather than abstract
inheritance in this instance, as the Place model already exists as a
concrete model, and I'm adding the Restaurant model. Also, for
database design reasons, I'd prefer to keep the tables separate.


On Nov 6, 9:02 pm, Michael Sprayberry <michaelspraybe...@yahoo.com>
wrote:
> Hello,
>  
> I was just going through this posting and I noticed that you are using concrete inheritance in your calling of
>  
> class Place(models.Model):
>                    name = models.CharField(max_length=50)
>                    address = models.CharField(max_length=80)
>  
> class Restaurant(Place):
>                    place = models.OneToOneField(Place, parent_link=True,
>                    related_name='restaurant')
>                    serves_hot_dogs = models.BooleanField()
>                    serves_pizza = models.BooleanField()
>  
> Try
>  
> class Place(models.Model):
>                    name = models.CharField(max_length=50)
>                    address = models.CharField(max_length=80)
>  
>                    class Meta:
>                             abstract = True
>  
> class Restaurant(Place):
>                    place = models.OneToOneField(Place, parent_link=True,
>                    related_name='restaurant')
>                    serves_hot_dogs = models.BooleanField()
>                    serves_pizza = models.BooleanField()
>  
>  
> This is abstract inheritance and any model inheriting Place will get a copy of Place's fields onto the child object instead of getting another Table placed there.
>  
> Michael 
>
> --- On Sat, 11/6/10, ringemup <ringe...@gmail.com> wrote:
> > > > > > > django-users...@googlegroups.com<django-users%2Bunsu...@googlegroups.com>
> > > <django-users%2Bunsu...@googlegroups.com<django-users%252Buns...@googlegroups.com>
>
> > > > > <django-users%2Bunsu...@googlegroups.com<django-users%252Buns...@googlegroups.com>
> > > <django-users%252Buns...@googlegroups.com<django-users%25252Bun...@googlegroups.com>
>
> > > > > > > .
> > > > > > > For more options, visit this group at
> > > > > > >http://groups.google.com/group/django-users?hl=en.
>
> > > > > --
> > > > > You received this message because you are subscribed to the Google
> > > Groups
> > > > > "Django users" group.
> > > > > To post to this group, send email to django...@googlegroups.com.
> > > > > To unsubscribe from this group, send email to
> > > > > django-users...@googlegroups.com<django-users%2Bunsu...@googlegroups.com>
> > > <django-users%2Bunsu...@googlegroups.com<django-users%252Buns...@googlegroups.com>
>
> > > > > .
> > > > > For more options, visit this group at
> > > > >http://groups.google.com/group/django-users?hl=en.
>
> > > --
> > > You received this message because you are subscribed to the Google Groups
> > > "Django users" group.
> > > To post to this group, send email to django...@googlegroups.com.
> > > To unsubscribe from this group, send email to
> > > django-users...@googlegroups.com<django-users%2Bunsu...@googlegroups.com>
> > > .
> > > For more options, visit this group at
> > >http://groups.google.com/group/django-users?hl=en.
>
> --
> You received this message because you are subscribed to the Google Groups "Django users" group.
> To post to this group, send email to django...@googlegroups.com.
> To unsubscribe from this group, send email to django-users...@googlegroups.com.

ringemup

unread,
Nov 7, 2010, 10:19:09 AM11/7/10
to Django users
Thank you for the pointer to ticket #11618, Stefan. At this point, I
don't care if the workaround is ugly -- if it actually works, at least
I'll be able to move forward with this project. I'll test it out and
check back in to confirm whether it does.

Luc Saffre

unread,
Feb 25, 2011, 1:43:14 AM2/25/11
to django...@googlegroups.com
I had now a similar problem and started to write a generic solution
which is certainly not perfect, but works for me. Here is a detailed
description:

http://lino.saffre-rumma.net/autodoc/lino.test_apps.1.html

Looking forward to any comments.
Luc

Malik Rumi

unread,
Jun 22, 2016, 6:45:23 PM6/22/16
to Django users
This thread is obviously very old, but since it helped me find the answer, I thought I would share that for the benefit of others mystified by the lack of information on https://docs.djangoproject.com/en/1.9/topics/db/models/#multi-table-inheritance.

It turns out that although Derek's link to the docs was way out of date (the page does not exist anymore) his solution was still close to correct. The full documentation can be found here: https://docs.djangoproject.com/en/1.9/topics/db/examples/one_to_one/

Davi P

unread,
Jun 24, 2016, 6:57:39 AM6/24/16
to Django users
I think the problem in the original post was making Restaurant a subclass of Place:

class Restaurant(Place):

That's why Django gave an error for the missing name and address.

With a OneToOne relation pointing from each Restaurant object to a Place object, there's no need to include those fields in Restaurant. So it's not a subclass of Place:

class Restaurant(models.Model):

(As shown in the One-to-one example. Thanks for the updated link.) 

The other alternative is to keep the subclass, and remove the OneToOneField. Then do:

for place in Place.objects.all(): 
  restaurant = Restaurant(**{ 
    'name': place.name
    'address': place.address, 

    'serves_hot_dogs': False, 
    'serves_pizza': True, 
  }) 
  restaurant.save() 

--Davi

sp...@gorrog.org

unread,
Jan 3, 2017, 7:05:07 AM1/3/17
to Django users
Thanks Malik for the link to https://dateutil.readthedocs.io/en/stable/examples.html#relativedelta-examples
After many, many hours of being baffled at how to create a child instance from an existing parent, this answered my question.
Reply all
Reply to author
Forward
0 new messages