views:

52

answers:

3

Hello all,

I have an existing app with the following model

class Contact(models.Model):
     lastname = models.CharField(max_length=200)
     firstname = models.CharField(max_length=200)
     ...

class Journalist(Contact):
     pass

I have a Contact in my database and I would like that it becomes a Journalist.

In raw sql, it seems as simple as insert into app_journalist values (25624);. In this example 25624 is the id of the existing contact. It seems to work ok and the django app seems happy.

However, I would like to make the same thing with the django ORM. I have tried several thinks like forcing the journalist id (Journalist(id=25624)) but it creates a new contact rather than linking to the existing one.

Is it possible do to that with the Django ORM? How?

Thanks in advance for your help

+3  A: 

One way to solve this is (without changing your model structure) to set the contact_ptr attribute of the Journalist instance to the appropriate Contact instance. For e.g.

contact = Contact.objects.get(pk = 25624)
journalist = Journalist(contact_ptr = contact)
journalist.save()

This becomes easier to understand if you first look at the table app_journalist. It has but one column, contact_ptr_id. So when you execute insert into app_journalist values (25624) you are setting contact_ptr_id = 25624 at the SQL level. Correspondingly you should set the contact_ptr = <instance of Contact> at the ORM level.

Update

There are other ways to solve the problem but they would require changes to your existing models. As @bugspy.net pointed out you can use a generic relationship. Alternately you can declare an additional type field to specify whether the contact is a journalist, a colleague etc.

Update 2

Also take a look at this demo snippet (and the complete code) that lets you use polymorphic inheritance (SQLAlchemy already does this).

Update 3

As @luc himself pointed out (see comment below)

journalist = Journalist(contact_ptr = contact)

alone will not suffice. This will overwrite the firstname and lastname of the contact to "". To avoid this you have to explicitly assign each field to Journalist.

Manoj Govindan
It seems that this is not enough because the lastname and firstname of the contact are overwritten with empty values. It seems that I also have to copy field by field.for a in contact.__dict__: if a != '_state': journalist.__dict__[a] = contact.__dict__[a]Anyway, thanks for your precious help
luc
+2  A: 

Django's Contenttypes framework is really handy. You could use it to represent different contact types:

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic

class Contact(models.Model):
     lastname = models.CharField(max_length=200)
     firstname = models.CharField(max_length=200)
     content_object = generic.GenericForeignKey('content_type', 'object_id')
bugspy.net
Thanks. I'll check the contenttypes framework
luc
A: 

Inheritance at the model level is usually not such a good idea. Most ORM let you do it. Most ORM are even proud to be able to do it. At the model level, the famous "prefer composition over inheritance" is more true than ever.

In your case, instead of saying : "A journalist IS A person", you could instead say : "A person has a job, which in this case is journalist". This would be represented by a Person class composed with a Job class. One of the job could be "journalist".

The composition approach lets you have a person change job, or even have multiple jobs.

Of course, this doesnt directly answer your question, but the other answers are already pretty good !

Guillaume
I agree with you but it is an existing application and changing the model will not be easy
luc