Login

Custom Django manager that excludes subclasses

Author:
sciyoshi
Posted:
August 7, 2008
Language:
Python
Version:
.96
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. Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

Please login first before commenting.