thevengefulco
thevengefulco

Reputation: 309

Django Inheriting from classes

I have run into a problem developing my Django site.

from django.db import models

class TitlePost(models.Model):
    title_name = models.CharField(max_length=100, unique=True)
    title_body = models.TextField(max_length=30000)
    title_why = models.TextField(max_length=250, null=True)
    title_publication_date = models.DateTimeField('date')
    likes = models.IntegerField(default=0)
    dislikes = models.IntegerField(default=0)


    def __unicode__(self):
        return self.title_name


class TopTitlesPostPage(models.Model):
    title_post = models.OneToOneField(TitlePost)
    hello = models.CharField(max_length=100, unique=True)

    def __unicode__(self):
        return self.hello

class NewTitlesPostPage(models.Model):
    title_post = models.OneToOneField(TitlePost)
    hello = models.CharField(max_length=100, unique=True)

    def __unicode__(self):
        return self.hello

Why don't TopTitlesPostPage and NewTitlesPostPage inherit all the attributes from TitlePost? For instance, if I try to call the likes in my template using TopTitlesPostPage, it will not execute because the likes attribute is not inherited. Does OneToOneField have something to do with the problem? I did read that making TitlePost a meta class will help but I need it to have a table in my database. I actually want all of them to have a table in my data base. Then again, maybe I am approaching this the wrong way and I should use just TitlePost as a model to generate everything?

Upvotes: 1

Views: 304

Answers (3)

Sebastian Wozny
Sebastian Wozny

Reputation: 17526

The behaviour you would like to see is called multi table inheritance. Every child class internally ends up with the same thing that you wrote, so with a one to one field to the base class TitlePost, but it's internally managed by django.

If you do multiple inheritance like the code below you will be able to write:

k=TopTitlesPostPage.objects.create(hello="Hello",title_name="Heh")

That means the fields will be directly accessible.

from django.db import models

class TitlePost(models.Model):
    title_name = models.CharField(max_length=100, unique=True)
    title_body = models.TextField(max_length=30000)
    title_why = models.TextField(max_length=250, null=True)
    title_publication_date = models.DateTimeField('date')
    likes = models.IntegerField(default=0)
    dislikes = models.IntegerField(default=0)


    def __unicode__(self):
        return self.title_name


class TopTitlesPostPage(TitlePost):
    hello = models.CharField(max_length=100, unique=True)

    def __unicode__(self):
        return self.hello

class NewTitlesPostPage(TitlePost):
    hello = models.CharField(max_length=100, unique=True)

    def __unicode__(self):
        return self.hello

In case you are never actually going to reference the base class TitlePost, but only its children it might be more appropriate to make `TitlePost abstract:

  class TitlePost(models.Model):
    title_name = models.CharField(max_length=100, unique=True)
    title_body = models.TextField(max_length=30000)
    title_why = models.TextField(max_length=250, null=True)
    title_publication_date = models.DateTimeField('date')
    likes = models.IntegerField(default=0)
    dislikes = models.IntegerField(default=0)

    class Meta:
        abstract = True
    def __unicode__(self):
        return self.title_name

Making TitlePostabstract will omit the creation of the table TitlePostin the database, and the child models will end up with the fields of the base class inserted into their own tables seperately. If the base class is just for factoring out common functionality this is the preferred way to go.

For huge queries this will also make a difference in performance because the ORM will need to do less JOINoperations.

It's not possible to install Foreign Keys to abstract models in Django. You can however install Foreign Keys to a non abstract base class. The only limitation is that the reverse Foreign Key relation will return the base class instances. You can circumvent this limitation by using django-polymorphic.

Django Polymorphic allows you to query the base class objects but retrieves the child class instances:

>>> Project.objects.create(topic="Department Party")
>>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")

>>> Project.objects.all()
[ <Project:         id 1, topic "Department Party">,
  <ArtProject:      id 2, topic "Painting with Tim", artist "T. Turner">,
  <ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]

To use django polymorphic you only need to declare your models with Polymorphic Model as base class:

from django.db import models
from polymorphic import PolymorphicModel

class ModelA(PolymorphicModel):
    field1 = models.CharField(max_length=10)

class ModelB(ModelA):
    field2 = models.CharField(max_length=10)

class ModelC(ModelB):
    field3 = models.CharField(max_length=10)

Foreign keys will also return the child class instances, which is really cool if you're trying to be polymorphic.

# The model holding the relation may be any kind of model, polymorphic or not
class RelatingModel(models.Model):
    many2many = models.ManyToManyField('ModelA')  # ManyToMany relation to a polymorphic model

>>> o=RelatingModel.objects.create()
>>> o.many2many.add(ModelA.objects.get(id=1))
>>> o.many2many.add(ModelB.objects.get(id=2))
>>> o.many2many.add(ModelC.objects.get(id=3))

>>> o.many2many.all()
[ <ModelA: id 1, field1 (CharField)>,
  <ModelB: id 2, field1 (CharField), field2 (CharField)>,
  <ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]

Take into account that these queries will be slightly less performant.

Upvotes: 5

Abhishek
Abhishek

Reputation: 3066

U need to extend the classes like follows:

class TopTitlesPostPage(TitlePost):

U can add more and inherit from multiple models just by mentionin g all the models comma separated! This all the fields from the models will be created in the child class as well

EDIT:

The way i would do it is to create an Abstract class which contains all your common fields and extend it into your TitlePost, TopTitlesPostPagea and NewTitlesPostPage

Upvotes: 1

Stefan Bossbaly
Stefan Bossbaly

Reputation: 6804

You need to have TopTitlesPostPage and NewTitlesPostPage extend the base class of TitlePost like so ...

class TopTitlesPostPage(models.Model)

You don't need a OneToOneField if you are inheriting from the base class, since the attributes of TitlePost will be available to you in the subclass. If you want to make TitlePost abstract (you can not declare an instance of that class, only inherit from it) you have to add it to the meta class

class TitlePost(models.Model):
    class Meta:
        abstract = True

Here is a link to the documentation.

Upvotes: 0

Related Questions