Login

Polymorphic inheritance ala SQLAlchemy

Author:
gsakkis
Posted:
October 3, 2010
Language:
Python
Version:
1.2
Tags:
inheritance polymorphic
Score:
3 (after 3 ratings)

This is a different take on polymorphic inheritance, inspired by SQLAlchemy's approach to the problem.

The common Django approach (e.g. snippets 1031 & 1034, django_polymorphic) is to use a foreign key to ContentType on the parent model and override save() to set the right content type automatically. That works fine but it might not always be possible or desirable, for example if there is another field that determines the "real type" of an instance.

In contrast this snippet (which is actually posted and maintained at gist.github) allows the user to explicitly specify the field that determines the real type of an instance. The basic idea and the terminology (polymorphic_on field, polymorphic_identity value) are taken from SQLAlchemy.

Some other features:

  • It works for proxy child models too, with almost no overhead compared to non-polymorphic managers (since there's no need to hit another DB table as for multi-table inheritance).

  • It does not override the default (or any other) model Manager. Regular (non-polymorphic) managers and querysets are still available if desired.

  • It does not require extending a custom Model base class, using a custom metaclass, monkeypatching the models or any kind of magic.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# just a demo example
# the actual code is at http://gist.github.com/608595

from django.db import models
from polymorphic import polymorphic_manager


class Player(models.Model):
    hitpoints = models.PositiveIntegerField(default=100)

    # polymorphic_on field
    race = models.SmallIntegerField(choices=enumerate(['Elf', 'Troll', 'Human']))

    # keep the default (non-polymorphic) manager
    objects = models.Manager()

    # a new manager polymorphic on Player.race
    objects_by_race = polymorphic_manager(on=race)

    def __unicode__(self):
        return u'Player(%s)' % self.pk


class Elf(Player):
    bows = models.PositiveIntegerField(default=0)

    # polymorphic manager for race=0
    objects = Player.objects_by_race.polymorphic_identity(0)

    def __unicode__(self):
        return u'Elf(%s)' % self.pk


class Troll(Player):
    axes = models.PositiveIntegerField(default=0)

    # polymorphic manager for race=1
    objects = Player.objects_by_race.polymorphic_identity(1)

    def __unicode__(self):
        return u'Troll(%s)' % self.pk


class Human(Player):

    # polymorphic manager for race=2
    objects = Player.objects_by_race.polymorphic_identity(2)

    class Meta:
        proxy = True

    def __unicode__(self):
        return u'Human(%s)' % self.pk


def test():
    from random import choice
    Player.objects.all().delete()

    # create a bunch of random type players
    for i in xrange(10):
        choice([Elf, Troll, Human]).objects.create()

    # retrieval through the polymorphic manager returns instances of the right class
    print "Automatically downcast players:", Player.objects_by_race.all()

    # retrieval through default Player manager returns Player instances as usual
    players = Player.objects.all()
    print "Non-downcast players:", players

    # but they can be explicitly downcast to the right class
    print "Explicitly downcast players:", map(Player.objects_by_race.downcast, players)

    # retrieving the instances of a specific class works as expected
    print "Elfs:", Elf.objects.all()
    print "Trolls:", Troll.objects.all()
    print "Humans:", Human.objects.all()

test()

More like this

  1. Custom Django manager that excludes subclasses by sciyoshi 6 years, 6 months ago
  2. Ordered items in the database - alternative by Leonidas 7 years, 9 months ago
  3. Ordered items in the database by Leonidas 7 years, 9 months ago
  4. models.py with django_dag models for parts hierarchy by j_syk 3 years, 7 months ago
  5. Complex Formsets by smagala 6 years, 1 month ago

Comments

Please login first before commenting.