Login

Custom Django manager that excludes subclasses

Author:
sciyoshi
Posted:
August 7, 2008
Language:
Python
Version:
.96
Tags:
managers models model subclass manager inheritance subclasses
Score:
0 (after 0 ratings)

When you're using Django model inheritance, sometimes you want to be able to get objects of the base class that aren't instances of any of the subclasses. You might expect the obvious way of doing this, SuperModel.objects.filter(submodel__isnull=True), to work, but unfortunately it doesn't. (Neither does SuperModel.objects.filter(submodel__supermodel_ptr=None), or any other convoluted way I could think of doing it.) Here's a nicer approach for doing this.

The blog entry is here.

 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
from django.db import models, connection
from django.db.models import signals

class NoSubclassManager(models.Manager):
    """
    Custom manager that excludes subclasses.

    >>> class Place(models.Model):
    ...     address = models.CharField(max_length=30)
    ...     objects = models.Manager()
    ...     only = NoSubclassManager()
    ...     def __unicode__(self):
    ...         return self.address

    >>> class Restaurant(Place):
    ...     name = models.CharField(max_length=30)

    >>> class House(Place):
    ...     owner = models.CharField(max_length=30)

    >>> Place(address='123 Acme St.').save()
    >>> Restaurant(address='987 Pizza Rd.', name='PizzaPalace').save()
    >>> House(address='23 Joe Rd.', owner='Joe').save()

    # Place.objects gives every single Place, even Restaurants and Houses
    >>> Place.objects.all()
    [<Place: 123 Acme St.>, <Place: 987 Pizza Rd.>, <Place: 23 Joe Rd.>]

    # Place.only gives only Places that are neither Restaurants nor Houses
    >>> Place.only.all()
    [<Place: 123 Acme St.>]
    """
    def __init__(self, *args, **kwargs):
        super(NoSubclassManager, self).__init__(*args, **kwargs)
        self.excludes = []

    def _class_prepared(self, sender, **kwargs):
        # add the subclass to our list of excluded models
        if self.model in sender._meta.parents:
            self.excludes.append(sender)

    def contribute_to_class(self, model, name):
        super(NoSubclassManager, self).contribute_to_class(model, name)
        # connect the signal to pick up on subclasses
        signals.class_prepared.connect(self._class_prepared)

    def get_query_set(self):
        qn = connection.ops.quote_name
        return super(NoSubclassManager, self).get_query_set().extra(
            where=['''
                not exists (
                    select 1
                    from   %s
                    where  %s.%s = %s
                )
            ''' % (
                qn(model._meta.db_table),
                qn(model._meta.db_table),
                qn(model._meta.pk.column),
                qn(self.model._meta.pk.column)
            ) for model in self.excludes])

More like this

  1. Django Model Inheritance by srid 7 years, 2 months ago
  2. Ordered items in the database - alternative by Leonidas 7 years, 10 months ago
  3. Model inheritance with content type and inheritance-aware manager by dan90 6 years, 6 months ago
  4. Complex Formsets by smagala 6 years, 2 months ago
  5. Model manager with row caching by jobs@flowgram.com 6 years, 9 months ago

Comments

Please login first before commenting.