Login

Get derived model instance

Author:
werehuman
Posted:
September 18, 2013
Language:
Python
Version:
1.5
Score:
1 (after 1 ratings)

Get derived model without storing their names or content types in databases. You write only one line, it expands into only one SQL-query (with many LEFT OUTER JOIN's).

Model definition example:

class BaseModel(models.Model):
    foo = models.IntegerField(null=True)

    derived = DerivedManager()


class FirstChild(BaseModel):
    bar = models.IntegerField(null=True)


class SecondChild(BaseModel):
    baz = models.IntegerField(null=True)

How to use:

>>> f = FirstChild.objects.create()
>>> s = SecondChild.objects.create()
>>> print list(BaseModel.objects.all()
[<BaseModel object 1>, <BaseModel object 2>]
>>> print list(BaseModel.derived.all()
[<FirstChild object 1>, <SecondChild object 2>]
>>> print BaseModel.derived.get(pk=s.pk)
<SecondChild object 2>
 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
from django.db.models import Manager
from django.db.models.query import QuerySet
from django.db.models.fields.related import SingleRelatedObjectDescriptor


class _DerivedNamesMixin(object):
    def _get_derived_names(self):
        return [k for k, v in self.model.__dict__.iteritems()
                if isinstance(v, SingleRelatedObjectDescriptor)
                and issubclass(v.related.model, self.model)]


class DerivedQuerySet(_DerivedNamesMixin, QuerySet):
    def iterator(self):
        prefetched = super(DerivedQuerySet, self).select_related(
            *self._get_derived_names())
        for obj in super(DerivedQuerySet, prefetched).iterator():
            yield self.__get_derived(obj)

    def __get_derived(self, instance):
        from django.core.exceptions import ObjectDoesNotExist
        for derived_name in self._get_derived_names():
            try:
                return getattr(instance, derived_name)
            except ObjectDoesNotExist:
                pass
        return instance


class DerivedManager(_DerivedNamesMixin, models.Manager):
    def get_query_set(self, *args, **kwargs):
        return DerivedQuerySet(self.model, using=self._db).select_related(
            *self._get_derived_names())

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 3 months ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 3 months, 1 week ago
  3. Serializer factory with Django Rest Framework by julio 10 months, 1 week ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 10 months, 4 weeks ago
  5. Help text hyperlinks by sa2812 11 months, 3 weeks ago

Comments

arthur (on September 18, 2013):

The model definition example should probably be:

class BaseModel(models.Model):
    foo = models.IntegerField(null=True)

    derived = DerivedManager()

class FirstChild(BaseModel):
    bar = models.IntegerField(null=True)

class SecondChild(BaseModel):
    baz = models.IntegerField(null=True)

(FirstChild and SecondChild should inherit from BaseModel, not models.Model)

Otherwise, looks very useful, thanks!

#

Please login first before commenting.