Custom Django manager that excludes subclasses

 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 6 years, 3 months ago
  2. Ordered items in the database - alternative by Leonidas 6 years, 10 months ago
  3. Model inheritance with content type and inheritance-aware manager by dan90 5 years, 7 months ago
  4. Complex Formsets by smagala 5 years, 3 months ago
  5. Model manager with row caching by jobs@flowgram.com 5 years, 10 months ago

Comments

(Forgotten your password?)