Login

ParentModel and ChildManager for Model Inheritance

Author:
jpwatts
Posted:
September 8, 2008
Language:
Python
Version:
1.0
Tags:
model manager queryset inheritance
Score:
9 (after 9 ratings)

This is the approach I've taken to access instances of child models from their parent. Functionally it's very similar to snippets 1031 and 1034, but without the use of django.contrib.contenttypes.

Usage:

class Post(ParentModel):
    title = models.CharField(max_length=50)

    objects = models.Manager()
    children = ChildManager()

    def __unicode__(self):
        return self.title

    def get_parent_model(self):
        return Post

class Article(Post):
    text = models.TextField()

class Photo(Post):
    image = models.ImageField(upload_to='photos/')

class Link(Post):
    url = models.URLField()

In this case, the Post.children manager will return a queryset containing instances of the appropriate child model, rather than instances of Post.

>>> Post.objects.all()
[<Post: Django>, <Post: Make a Tumblelog>, <Post: Self Portrait>]

>>> Post.children.all()
[<Link: Django>, <Article: Make a Tumblelog>, <Photo: Self Portrait>]
 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
from django.db import models
from django.db.models.query import QuerySet


class ChildQuerySet(QuerySet):
    def iterator(self):
        for obj in super(ChildQuerySet, self).iterator():
            yield obj.get_child_object()


class ChildManager(models.Manager):
    def get_query_set(self):
        return ChildQuerySet(self.model)


class ParentModel(models.Model):
    _child_name = models.CharField(max_length=100, editable=False)

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        self._child_name = self.get_child_name()
        super(ParentModel, self).save(*args, **kwargs)

    def get_child_name(self):
        if type(self) is self.get_parent_model():
            return self._child_name
        return self.get_parent_link().related_query_name()

    def get_child_object(self):
        return getattr(self, self.get_child_name())

    def get_parent_link(self):
        return self._meta.parents[self.get_parent_model()]

    def get_parent_model(self):
        raise NotImplementedError

    def get_parent_object(self):
        return getattr(self, self.get_parent_link().name)

More like this

  1. Multiple querysets by t_rybik 5 years, 2 months ago
  2. Custom managers with chainable filters by itavor 7 years, 2 months ago
  3. Finding related objects for instances in a queryset by akaihola 4 years, 1 month ago
  4. Bitwise operator queryset filter by hgeerts@osso.nl 4 years, 11 months ago
  5. Polymorphic inheritance ala SQLAlchemy by gsakkis 4 years, 6 months ago

Comments

donspaulding (on September 8, 2008):

Nice.

1031 and 1034 solved the problem, but this was the usage I was looking to get from the solution. Thanks!

#

neithere (on November 2, 2008):

Thanks, excellent snippet!

#

carljm (on February 5, 2009):

Slick.

#

BUZZY (on December 3, 2012):

That's exactly what I was looking for, thanks!

#

Please login first before commenting.