type-field model inheritance

141 views
Skip to first unread message

Craig de Stigter

unread,
Feb 17, 2011, 5:08:57 AM2/17/11
to django-d...@googlegroups.com
Hi folks

Ever since Django started supporting various types of model inheritance I've wondered why it lacks the kind that I would find most useful: python behaviour differentiated based on the value of a field.

I'll explain with an example. Here's what I'd like to do:

class Datasource(models.Model):
    type = models.ModelTypeField()
    uri = models.CharField(max_length=256)
    # common behavior in the superclass
    def __repr__(self):
        return u'<%s: %s>' % (self.__class__.__name__, self.uri)
class HttpDatasource(Datasource):
    # custom behaviour in the subclasses
    def get_filename(self):
        return self.uri.rsplit("/", 1)[-1] 
class ZipfileDatasource(Datasource):
    def get_filename(self):
        files = zipfile.list(self.uri)
        return files[0].rsplit('/', 1)[-1]

>>> zip = ZipfileDatasource.objects.create(uri="/path/to/foo.zip")
>>> uri = UriDatasource.objects.create(uri="http://example.com/foo.txt") 
>>> Datasource.objects.all()
[<ZipfileDatasource: /path/to/foo.zip>, <UriDatasource: http://example.com/foo.txt>] 
>>>ZipfileDatasource.objects.all()
[<ZipfileDatasource: /path/to/foo.zip>] 
>>> Datasource.objects.all().values_list('type', flat=True)
[u'myapp.models.ZipfileDatasource', u'myapp.models.UriDatasource']

These are quite similar to proxy models, but vary in their queryset behaviour - the generic Datasource queryset has mixed types and the concrete querysets are always filtered by type.

This is far more useful than proxy models, since the concrete types of each table are known. It's also better than making an abstract model and subclassing it, because now all the objects are in the same table and you can iterate over them all at once if you want. Adding more types is easy, since there are no schema changes (with abstract models you'd have to add a new table for each type).

It's possible that proxy models could be extended to do this without breaking existing code. I'm not sure yet.

I'm thinking of diving into this. Does anyone have any suggestions? Or, perhaps there's a reason why this hasn't been done previously that you more experienced gurus could illuminate?

Thanks in advance

Craig de Stigter
Maintainer of django-mptt and full time dev at koordinates.com

Craig de Stigter

unread,
Mar 2, 2011, 9:07:20 PM3/2/11
to django-d...@googlegroups.com
I realise everyone's been busy with getting 1.3 ready, but doesn't anyone have thoughts on this? It's been two weeks ...

Thanks
Craig

Stephen Burrows

unread,
Mar 3, 2011, 12:37:38 AM3/3/11
to Django developers
It sounds like you want to make polymorphic models - queries that
retrieve various types of objects. There's an implementation of this:
https://github.com/bconstantin/django_polymorphic

Russell Keith-Magee

unread,
Mar 3, 2011, 6:55:44 AM3/3/11
to django-d...@googlegroups.com
On Thu, Mar 3, 2011 at 10:07 AM, Craig de Stigter <crai...@gmail.com> wrote:
> I realise everyone's been busy with getting 1.3 ready, but doesn't anyone
> have thoughts on this? It's been two weeks ...

Hi Craig,

Thanks for the suggestion. As you've noted, we've been busy with the
1.3 release, which has been consuming a lot of the core team's time.
Sometimes posts fall between the cracks; your post is one such victim.

The other reason that you haven't got much traction is that this isn't
the first time your idea has been proposed. In fact, this suggestion
one of the design options on the table at the time that model
inheritance was originally proposed. It was rejected at the time on
the grounds that it was too much overhead to impose on a general
inheritance solution (i.e., DB columns aren't free, and many
applications of inheritance don't require the extra column). Search
django-dev for "CORBA narrow" for the full discussion and reasoning.

Since then, a couple of people have addressed this issue using mixins;
this enables you to get the subclassing+narrowing goodness by simply
annotating a class heirarchy. django_polymorphic (given in another
reply) is one such example; there are others.

Personally, I'd eventually like to see a contrib package that
contained extensions like this -- polymorphic models, tree
implementations and so on -- model extensions to represent common data
usage patterns. Ideally, this contrib app would be developed in the
same way that contrib.messages was handled -- a small team doing a
survey of the community to find all the model extensions like this
that exist (e.g., mptt, treebeard, polymorphic, and so on), and
pulling them together into a single project that covers all the use
cases spanned by each individual project. Then, once a single spanning
implementation exists (and is documented, tested, etc), it could be
proposed for inclusion in trunk.

Yours,
Russ Magee %-)

Craig de Stigter

unread,
Mar 4, 2011, 1:03:57 AM3/4/11
to django-d...@googlegroups.com
Hi guys

Thanks for pointing those out. I knew I couldn't have been the first to want this. I guess I just didn't know the right words to search for here.

It looks like django_polymorphic does what I want. I'm not yet sure why it says it takes one query per type of model in a queryset. Unless it is talking about multi-table inheritance, in which each type would require a different join. But I'll look in more detail in the next few days and no doubt it will become clear.

Thanks for the reference to CORBA narrow too. Skimming the thread now.

Regards
Craig

Simon Meers

unread,
Mar 4, 2011, 3:27:27 AM3/4/11
to django-developers
On 4 March 2011 17:03, Craig de Stigter <crai...@gmail.com> wrote:
> Hi guys
>
> Thanks for pointing those out. I knew I couldn't have been the first to want
> this. I guess I just didn't know the right words to search for here.
> It looks like django_polymorphic does what I want. I'm not yet sure why it
> says it takes one query per type of model in a queryset. Unless it is
> talking about multi-table inheritance, in which each type would require a
> different join. But I'll look in more detail in the next few days and no
> doubt it will become clear.

+1 for better polymorphic support in Django core; it is a very common
problem which could do with an efficient and elegant solution.
Regarding efficiency, if you can keep track of your subclasses
effectively (potentially using a registry if not introspection), you
can use select_related to retrieve heterogeneous multi-table
inheritance models in a single query (see example in [1]). I haven't
looked deeply enough into django_polymorphic to see if it includes
such optimisations. I'm sure we could further tweak the internals to
make things more efficient and developer-friendly in this regard.

Cheers,
Simon

[1] http://stackoverflow.com/q/5175009/284164

Carl Meyer

unread,
Mar 4, 2011, 11:29:29 PM3/4/11
to Django developers
Hi Craig,

On Mar 4, 1:03 am, Craig de Stigter <craig...@gmail.com> wrote:
> It looks like django_polymorphic does what I want. I'm not yet sure why it
> says it takes one query per type of model in a queryset. Unless it is
> talking about multi-table inheritance, in which each type would require a
> different join. But I'll look in more detail in the next few days and no
> doubt it will become clear.

Since multi-table-inheritance is the only kind of inheritance (apart
from abstract/proxy) supported by Django's ORM, I don't know what
other type of inheritance django_polymorphic would be referring to...

Carl

Carl Meyer

unread,
Mar 4, 2011, 11:32:17 PM3/4/11
to Django developers
Hi Simon,

On Mar 4, 3:27 am, Simon Meers <drme...@gmail.com> wrote:
> +1 for better polymorphic support in Django core; it is a very common
> problem which could do with an efficient and elegant solution.
> Regarding efficiency, if you can keep track of your subclasses
> effectively (potentially using a registry if not introspection), you
> can use select_related to retrieve heterogeneous multi-table
> inheritance models in a single query (see example in [1]). I haven't
> looked deeply enough into django_polymorphic to see if it includes
> such optimisations. I'm sure we could further tweak the internals to
> make things more efficient and developer-friendly in this regard.

FWIW, django-model-utils [1] includes an InheritanceManager that
implements polymorphic queries in a single query via select_related.

[1] https://github.com/carljm/django-model-utils

Simon Meers

unread,
Mar 5, 2011, 12:51:24 AM3/5/11
to django-developers
Hi Carl,

> FWIW, django-model-utils [1] includes an InheritanceManager that
> implements polymorphic queries in a single query via select_related.

Ah, there goes my theory that I thought of it first :) And of course
introspection of subclasses via the reverse OneToOne descriptors is
perfect.

Craig de Stigter

unread,
Mar 5, 2011, 1:04:37 AM3/5/11
to django-d...@googlegroups.com
Since multi-table-inheritance is the only kind of inheritance (apart 
from abstract/proxy) supported by Django's ORM, I don't know what 
other type of inheritance django_polymorphic would be referring to... 
As per my original post, I want to store everything in one table (all subclasses have the same fields). I just want to have different python behaviour for subclasses.

Basically proxy models but with a known type and polymorphic querysets.

I still haven't looked into django_polymorphic to see if it does that, but it seems like it could easily... Hopefully I'll get time to play with it in the next couple days.

Regards
Craig
Reply all
Reply to author
Forward
0 new messages