Easier custom Model Manager Chaining

 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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from types import ClassType
from django.db.models.manager import Manager
from django.db.models.query import QuerySet


def manager_from(*mixins, **kwds):
    '''
    Returns a Manager instance with extra methods, also available and
    chainable on generated querysets.

    :param mixins: Each ``mixin`` can be either a class or a function. The
        generated manager and associated queryset subclasses extend the mixin
        classes and include the mixin functions (as methods).

    :keyword queryset_cls: The base queryset class to extend from
        (``django.db.models.query.QuerySet`` by default).

    :keyword manager_cls: The base manager class to extend from
        (``django.db.models.manager.Manager`` by default).
    '''
    # collect separately the mixin classes and methods
    bases = [kwds.get('queryset_cls', QuerySet)]
    methods = {}
    for mixin in mixins:
        if isinstance(mixin, (ClassType, type)):
            bases.append(mixin)
        else:
            try: methods[mixin.__name__] = mixin
            except AttributeError:
                raise TypeError('Mixin must be class or function, not %s' %
                                mixin.__class__)
    # create the QuerySet subclass
    id = hash(mixins + tuple(kwds.iteritems()))
    new_queryset_cls = type('Queryset_%d' % id, tuple(bases), methods)
    # create the Manager subclass
    bases[0] = manager_cls = kwds.get('manager_cls', Manager)
    new_manager_cls = type('Manager_%d' % id, tuple(bases), methods)
    # and finally override new manager's get_query_set
    super_get_query_set = manager_cls.get_query_set
    def get_query_set(self):
        # first honor the super manager's get_query_set
        qs = super_get_query_set(self)
        # and then try to bless the returned queryset by reassigning it to the
        # newly created Queryset class, though this may not be feasible
        if not issubclass(new_queryset_cls, qs.__class__):
            raise TypeError('QuerySet subclass conflict: cannot determine a '
                            'unique class for queryset instance')
        qs.__class__ = new_queryset_cls
        return qs
    new_manager_cls.get_query_set = get_query_set
    return new_manager_cls()

#==== example ==================================================================

from datetime import datetime
from django.db import models
from django.contrib.auth.models import User

class AuthorMixin(object):
    def by_author(self, user):
        return self.filter(user=user)

class PublishedMixin(object):
    def published(self):
        return self.filter(published__lte=datetime.now())

def unpublished(self):
    return self.filter(published__gte=datetime.now())

class CustomManager(Manager):
    def get_query_set(self):
        return super(CustomManager, self).get_query_set().order_by('-published')

class Post(models.Model):
    user = models.ForeignKey(User)
    published = models.DateTimeField()

    objects = manager_from(AuthorMixin, PublishedMixin, unpublished,
                              manager_cls=CustomManager)


print Post.objects.by_author(user=12).unpublished().query

More like this

  1. Custom Model Manager Chaining by hunterford 2 years, 11 months ago
  2. more on manager methods by grahamu 6 years, 3 months ago
  3. QuerySetManager - easily add new QuerySet methods using a Model inner class by simon 5 years, 1 month ago
  4. Polymorphic inheritance ala SQLAlchemy by gsakkis 2 years, 8 months ago
  5. See what depends on a given model by jdunck 2 years, 4 months ago

Comments

carljm (on July 20, 2010):

Brilliant! I find snippets like this easier to use if they are incorporated in a pip-installable Python project: have you released this code in any such project? If not, would you be open to its (fully credited) inclusion in django-model-utils (http://bitbucket.org/carljm/django-model-utils)?

#

gsakkis (on July 21, 2010):

@carljm, no it's not released anywhere; feel free to include it in your project with proper attribution.

#

gsakkis (on July 21, 2010):

2010-07-22: Fixed bug that was effectively ignoring the get_query_set of a passed custom manager. Now the parent get_query_set is honored, unless there is a conflict between the type of the returned queryset and the dynamically generated QuerySet subclass, in which case a TypeError is raised.

#

7times9 (on July 26, 2010):

Indentation of lines 67 & 68 needs a tab to the right?

#

7times9 (on July 26, 2010):

Sorry, please ignore my comment above.

It's clever how you can pass in a function or a class :-)

#

carljm (on July 30, 2010):

@gsakkis: thanks, added

#

siblek31 (on April 13, 2013):

Perancis memiliki anggaran dunia terbesar kelima nominal militer, [13] serta (dalam hal personil) militer terbesar di Uni Eropa, [rujukan?] Kekuatan deployable terbesar ketiga di NATO, dan militer-26 terbesar di dunia. Perancis juga memiliki stockpile terbesar ketiga senjata nuklir di dunia [14] - dengan sekitar 300 hulu ledak aktif sejak tips cepat hamil 25 Mei 2010 -. Dan dunia terbesar kedua diplomatik korps (di belakang Amerika Serikat) [15] Perancis adalah anggota pendiri PBB, salah satu dari lima anggota tetap Dewan Keamanan PBB, dan anggota Francophonie, teknisi komputer G8, G20, NATO, OECD, WTO, dan Uni Latin. Ini juga merupakan pendiri dan negara anggota terkemuka dari Uni Eropa dan negara Uni Eropa kursus bahasa inggris murah terbesar berdasarkan wilayah. [16] Pada 2013, Prancis tercatat 20 cara mendapatkan uang dari internet pada Indeks Pembangunan Manusia dan, pada tahun 2010, 24 pada Indeks Persepsi Korupsi.

#

(Forgotten your password?)