views:

208

answers:

2

First of all, some links to pages I've used for reference: A SO question, and the Django docs on generic relations and multi-table inheritance.

So far, I have a multi-table inheritance design set up. Objects (e.g: Car, Dog, Computer) can inherit an Item class. I need to be able to retrieve Items from the DB, get the subclass, and do stuff with it. My design doesn't allow for retrieving the different kinds of objects one by one, so I need to use the Item container to wrap them all into one. Once I have the Item, the Django docs say I can get the subclass by referencing the attribute with the name of the model (e.g: myitem.car or myitem.computer).

I don't know which type of object my item is referencing, so how can I get the child? Is there a built in way to do this? Here are some other ideas that I had: (some crazier than others)

  1. I was thinking I could add some sort of GenericForeignKey to Item that references the child, but I doubt it is even legal for a parent class to relate via a ForeignKey to a child class.
  2. I suppose I could have a ForeignKey(ContentType) in the Item class, and find the attribute of Item to get the child based on the ContentType's name.
  3. Finally, although an ugly method, I might be able to keep a list of object types, and try each as an attribute until a DoesNotExist error is not thrown.

As you can see, these proposed solutions are not that elegant, but I'm hoping I won't have to use one of them and someone here might have a better suggestion.

Thanks in advance

A: 

It would be better to compose the models of an Item model and an ItemType model. Subclassing models sounds nice and is useful in a few edge cases, but generally, it is safest and most efficient to stick to tactics that work with your database, rather than against it.

Jeff Ober
How is this going against the database?
T. Stone
Object oriented concepts such as inheritance are not supported at the database level. An ORM can provide some neat ways to bolt the features on, but they end up resulting in problems such as those above. Django implements this by creating extension tables with a ForeignKey back, but this lacks any simple method of introspection. You end up with a long series of if/else or try/except blocks.
Jeff Ober
Well the long series of if/else or try/except blocks is not needed as demonstrated above. I can't really imagine how trying to do this at the Database level would be any nicer.
Cory Walker
+1  A: 

I have done something similar to method 2 in one of my projects:

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

class BaseModel(models.Model):
    type = models.ForeignKey(ContentType,editable=False)
    # other base fields here

    def save(self,force_insert=False,force_update=False):
        if self.type_id is None:
            self.type = ContentType.objects.get_for_model(self.__class__)
        super(BaseModel,self).save(force_insert,force_update)

    def get_instance(self):
        return self.type.get_object_for_this_type(id=self.id)
Wogan
This would work in my situation, but is there not a way to do this with multi-table inheritance? I know I could use abstract inheritance with the above code, but IMHO true multi-table inheritance would be cleaner. That may just be me being picky though :)
Cory Walker
What makes you say this isn't multi-table inheritance?
Wogan
I have an important correction to this. 'type_ptr' needs to be changed to 'type_id'. The purpose of the line is to check if the ContentType has already been assigned and written to the database. The database field is type_id and not type_ptr. This difference is important because if you were working with a BaseModel and called save(), the type would be reset to BaseModel and the actual type would be cleared. Otherwise it would go unnoticed, but it can get nasty if the above happens.
Cory Walker
Updated, thanks.
Wogan
I actually had to change it to "if self.type_id == None:". The other correction was a typo and it will never run through. With the previous correction I mentioned, the if will never evaluate and the type will not be assigned. If you use "if self.type_id == None:", the type will only be updated if the type has not already been assigned.
Cory Walker