I have a Django app that has a number of different models, all with a bunch of common data. Ultimately, I think this question comes down to a decision between inheritance and composition. My current implementation is something like this:
class Thing(models.Model):
foo = models.FooField()
bar = models.BarField()
type = models.CharField()
class A(CommonStuff):
aFoo = models.FooField()
class B(CommonStuff):
bFoo = models.FooField()
With this model, I'm able to query for a Thing
using the Thing model's manager. Using the type field on Thing
, I can get the child object data by looking at the type
field, which contains either 'a' or 'b', and then asking for (i.e.) thing.a.aFoo
. This is a feature I like because it's a fairly common operation in my app to get a list of all Thing
objects.
I see a couple couple issues here. First, the type
field seems unnecessary. Is there way to get at the child data without having to first look up the type? It seems like I could work around this with an instance method that returned the correct object given its type value, or if I really wanted to get rid of the type field, I could iterate over each of the reverse relation fields on Thing
, looking for one that doesn't raise a DoesNotExist
exception. This feels quite brittle to me though. If I add a new 'subthing' C
, I have to update Thing
to look for the new type. I could fix this by making Thing
and abstract model. That way, A
and B
get all the fields of Thing
and I avoid having to use the type
field. Problem, though, is that I lose the ability to perform queries for all Thing
objects.
Another model I'm thinking about sort of flips this one on its head by turning the data in Thing
into a field on A
and B
.
class Thing(models.Model):
foo = models.FooField()
bar = models.BarField()
class A(models.Model):
aFoo = models.FooField()
thing = models.OneToOneField(Thing)
class B(models.Model):
bFoo = models.FooField()
thing = models.OneToOneField(Thing)
This version has a few benefits. It gets rid of the type
field on Thing
, and—at least to me—looks and feels cleaner and less brittle. The problem here, though, is the same as the problem with making Thing
abstract in the first version. I lose the ability to query all my 'subthings' together. I can do a query for A
objects or a query for B
objects, but not both. Can use this version of the model without having to sacrifice the ability to query for all 'subthings'? One possibility is to write a manager that queries both models and returns a QuerySet
of all the objects. Does that work?