views:

58

answers:

5

Let's say I have an abstract base class that looks like this:

class StellarObject(BaseModel):
  title = models.CharField(max_length=255)
  description = models.TextField()
  slug = models.SlugField(blank=True, null=True)

  class Meta:
    abstract = True

Now, let's say I have two actual database classes that inherit from StellarObject

class Planet(StellarObject):
  type = models.CharField(max_length=50)
  size = models.IntegerField(max_length=10)

class Star(StellarObject):
  mass = models.IntegerField(max_length=10)

So far, so good. If I want to get Planets or Stars, all I do is this:

Thing.objects.all() #or
Thing.objects.filter() #or count(), etc...

But what if I want to get ALL StellarObjects? If I do:

StellarObject.objects.all()

It of course returns an error, because an abstract class isn't an actual database object, and therefore cannot be queried. Everything I've read says I need to do two queries, one each on Planets and Stars, and then merge them. That seems horribly inefficient. Is that the only way?

+1  A: 

In this case I think there's no other way.

For optimization, you could avoid inheritance from abstract StellarObject and use it as separate table connected via FK to Star and Planet objects.

That way both of them would have ie. star.stellar_info.description.

Other way would be to add additional model for handling information and using StellarObject as through in many2many relation.

bx2
A: 

That seems horribly inefficient. Is that the only way?

As far as I know it is the only way with Django's ORM. As implemented currently abstract classes are a convenient mechanism for abstracting common attributes of classes out to super classes. The ORM does not provide a similar abstraction for querying.

You'd be better off using another mechanism for implementing hierarchy in the database. One way to do this would be to use a single table and "tag" rows using type. Or you can implement a generic foreign key to another model that holds properties (the latter doesn't sound right even to me).

Manoj Govindan
+1  A: 

Don't use an abstract base class if you need to query on the base. Use a concrete base class instead.

Carl Meyer
+1  A: 

At its root, this is part of the mismatch between objects and relational databases. The ORM does a great job in abstracting out the differences, but sometimes you just come up against them anyway.

Basically, you have to choose between abstract inheritance, in which case there is no database relationship between the two classes, or multi-table inheritance, which keeps the database relationship at a cost of efficiency (an extra database join) for each query.

Daniel Roseman
+1 for mentioning impedance mismatch. It is worth pondering when designing models.
Manoj Govindan
I ended up switching to multi-table-inheritance, as it seems to be the excepted way to achieve what I needed. Thanks all for the responses.
Kevin Whitaker
A: 

I would consider moving away from either an abstract inheritance pattern or the concrete base pattern if you're looking to tie distinct sub-class behaviors to the objects based on their respective child class.

When you query via the parent class -- which it sounds like you want to do -- Django treats the resulting ojects as objects of the parent class, so accessing child-class-level methods requires re-casting the objects into their 'proper' child class on the fly so they can see those methods... at which point a series of if statements hanging off a parent-class-level method would arguably be a cleaner approach.

If the sub-class behavior described above isn't an issue, you could consider a custom manager attached to an abstract base class sewing the models together via raw SQL.

If you're interested mainly in assigning a discrete set of identical data fields to a bunch of objects, I'd relate along a foreign-key, like bx2 suggests.

daemianmack