Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django model subclassing: Get the subclass by querying the superclass

The following code is given:

class BaseMedium(models.Model):
    title = models.CharField(max_length=40)
    slug = models.SlugField()

class A(BaseMedium):
    url = models.URLField()

class B(BaseMedium):
    email = models.EmailField()

I now want to query every BaseMedium.

b = BaseMedium.objects.all()

How do I print every information including the subclass fields without knowing what the subclass type is?

b[0].a would print the information if b[0] is actually related to an A instance but if it's related to B it would print an DoesNotExist Exception.

This makes sense but I'd like to have a common variable or method that returns the related object.

Maybe my Database layout isn't really great to query that way if so I'd be glad if you'd recommend a better layout.

I thought about using a GenericForeignKey

class Generic(models.Model):
    basemedium = models.ForeignKey('BaseMedium')
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    object = generic.GenericForeignKey('content_type', 'object_id')

but this solution seems to be to complicated and I think you guys have better solutions.

like image 616
rotrotrot Avatar asked Jun 24 '10 11:06

rotrotrot


2 Answers

The only way to do this is to explicitly store on the base model what type it is. So have a derived_type (or whatever) field on BaseMedium, and set it on save. Then you can have a get_derived_type method:

def get_derived_type(self):
    if self.derived_type ==  'A':
        return self.a
    elif self.derived_type == 'B':
        return self.b

and so on.

like image 33
Daniel Roseman Avatar answered Oct 29 '22 11:10

Daniel Roseman


You should check the solution posted by Carl Meyer some time ago. It internally uses the ContentType approach, but it encapsulates it very elegantly .

He also points to an alternative, and more efficient solution, that doesn't need to store an aditional field on the database, but it will only work for direct child classes. If you have several inheritance levels, the first solution is better.

like image 158
Filipe Correia Avatar answered Oct 29 '22 09:10

Filipe Correia