简体   繁体   中英

Django model inheritance problem. How to solve?

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

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 eg

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 .

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')

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 !

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM