File: managers.txt

package info (click to toggle)
python-django 1.0.2-1%2Blenny3
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 17,652 kB
  • ctags: 7,831
  • sloc: python: 46,969; makefile: 78; xml: 34; sql: 33; sh: 16
file content (223 lines) | stat: -rw-r--r-- 9,054 bytes parent folder | download
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
.. _topics-db-managers:

========
Managers
========

.. currentmodule:: django.db.models

.. class:: Manager()

A ``Manager`` is the interface through which database query operations are
provided to Django models. At least one ``Manager`` exists for every model in
a Django application.

The way ``Manager`` classes work is documented :ref:`topics-db-queries`; this
document specifically touches on model options that customize ``Manager``
behavior.

Manager names
=============

By default, Django adds a ``Manager`` with the name ``objects`` to every Django
model class. However, if you want to use ``objects`` as a field name, or if you
want to use a name other than ``objects`` for the ``Manager``, you can rename
it on a per-model basis. To rename the ``Manager`` for a given class, define a
class attribute of type ``models.Manager()`` on that model. For example::

    from django.db import models

    class Person(models.Model):
        #...
        people = models.Manager()

Using this example model, ``Person.objects`` will generate an
``AttributeError`` exception, but ``Person.people.all()`` will provide a list
of all ``Person`` objects.

.. _custom-managers:

Custom Managers
===============

You can use a custom ``Manager`` in a particular model by extending the base
``Manager`` class and instantiating your custom ``Manager`` in your model.

There are two reasons you might want to customize a ``Manager``: to add extra
``Manager`` methods, and/or to modify the initial ``QuerySet`` the ``Manager``
returns.

Adding extra Manager methods
----------------------------

Adding extra ``Manager`` methods is the preferred way to add "table-level"
functionality to your models. (For "row-level" functionality -- i.e., functions
that act on a single instance of a model object -- use :ref:`Model methods
<model-methods>`, not custom ``Manager`` methods.)

A custom ``Manager`` method can return anything you want. It doesn't have to
return a ``QuerySet``.

For example, this custom ``Manager`` offers a method ``with_counts()``, which
returns a list of all ``OpinionPoll`` objects, each with an extra
``num_responses`` attribute that is the result of an aggregate query::

    class PollManager(models.Manager):
        def with_counts(self):
            from django.db import connection
            cursor = connection.cursor()
            cursor.execute("""
                SELECT p.id, p.question, p.poll_date, COUNT(*)
                FROM polls_opinionpoll p, polls_response r
                WHERE p.id = r.poll_id
                GROUP BY 1, 2, 3
                ORDER BY 3 DESC""")
            result_list = []
            for row in cursor.fetchall():
                p = self.model(id=row[0], question=row[1], poll_date=row[2])
                p.num_responses = row[3]
                result_list.append(p)
            return result_list

    class OpinionPoll(models.Model):
        question = models.CharField(max_length=200)
        poll_date = models.DateField()
        objects = PollManager()

    class Response(models.Model):
        poll = models.ForeignKey(Poll)
        person_name = models.CharField(max_length=50)
        response = models.TextField()

With this example, you'd use ``OpinionPoll.objects.with_counts()`` to return
that list of ``OpinionPoll`` objects with ``num_responses`` attributes.

Another thing to note about this example is that ``Manager`` methods can
access ``self.model`` to get the model class to which they're attached.

Modifying initial Manager QuerySets
-----------------------------------

A ``Manager``'s base ``QuerySet`` returns all objects in the system. For
example, using this model::

    class Book(models.Model):
        title = models.CharField(max_length=100)
        author = models.CharField(max_length=50)

...the statement ``Book.objects.all()`` will return all books in the database.

You can override a ``Manager``\'s base ``QuerySet`` by overriding the
``Manager.get_query_set()`` method. ``get_query_set()`` should return a
``QuerySet`` with the properties you require.

For example, the following model has *two* ``Manager``\s -- one that returns
all objects, and one that returns only the books by Roald Dahl::

    # First, define the Manager subclass.
    class DahlBookManager(models.Manager):
        def get_query_set(self):
            return super(DahlBookManager, self).get_query_set().filter(author='Roald Dahl')

    # Then hook it into the Book model explicitly.
    class Book(models.Model):
        title = models.CharField(max_length=100)
        author = models.CharField(max_length=50)

        objects = models.Manager() # The default manager.
        dahl_objects = DahlBookManager() # The Dahl-specific manager.

With this sample model, ``Book.objects.all()`` will return all books in the
database, but ``Book.dahl_objects.all()`` will only return the ones written by
Roald Dahl.

Of course, because ``get_query_set()`` returns a ``QuerySet`` object, you can
use ``filter()``, ``exclude()`` and all the other ``QuerySet`` methods on it.
So these statements are all legal::

    Book.dahl_objects.all()
    Book.dahl_objects.filter(title='Matilda')
    Book.dahl_objects.count()

This example also pointed out another interesting technique: using multiple
managers on the same model. You can attach as many ``Manager()`` instances to
a model as you'd like. This is an easy way to define common "filters" for your
models.

For example::

    class MaleManager(models.Manager):
        def get_query_set(self):
            return super(MaleManager, self).get_query_set().filter(sex='M')

    class FemaleManager(models.Manager):
        def get_query_set(self):
            return super(FemaleManager, self).get_query_set().filter(sex='F')

    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        last_name = models.CharField(max_length=50)
        sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female')))
        people = models.Manager()
        men = MaleManager()
        women = FemaleManager()

This example allows you to request ``Person.men.all()``, ``Person.women.all()``,
and ``Person.people.all()``, yielding predictable results.

If you use custom ``Manager`` objects, take note that the first
``Manager`` Django encounters (in the order in which they're defined
in the model) has a special status. Django interprets this first
``Manager`` defined in a class as the "default" ``Manager``, and
several parts of Django (though not the admin application) will use
that ``Manager`` exclusively for that model. As a result, it's often a
good idea to be careful in your choice of default manager, in order to
avoid a situation where overriding of ``get_query_set()`` results in
an inability to retrieve objects you'd like to work with.

Using managers for related object access
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

By default, Django uses a "bare" (i.e. default) manager when accessing related
objects (i.e. ``choice.poll``). If this default isn't appropriate for your
default manager, you can force Django to use a custom manager for related object
attributes by giving it a ``use_for_related_fields`` property::

    class MyManager(models.Manager)::
        use_for_related_fields = True
        ...
        

        ...

Custom managers and model inheritance
-------------------------------------

Class inheritance and model managers aren't quite a perfect match for each
other. Managers are often specific to the classes they are defined on and
inheriting them in subclasses isn't necessarily a good idea. Also, because the
first manager declared is the *default manager*, it is important to allow that
to be controlled. So here's how Django handles custom managers and
:ref:`model inheritance <model-inheritance>`:

    1. Managers defined on non-abstract base classes are *not* inherited by
       child classes. If you want to reuse a manager from a non-abstract base,
       redeclare it explicitly on the child class. These sorts of managers are
       likely to be fairly specific to the class they are defined on, so
       inheriting them can often lead to unexpected results (particularly as
       far as the default manager goes). Therefore, they aren't passed onto
       child classes.

    2. Managers from abstract base classes are always inherited by the child
       class, using Python's normal name resolution order (names on the child
       class override all others; then come names on the first parent class,
       and so on). Abstract base classes are designed to capture information
       and behaviour that is common to their child classes. Defining common
       managers is an appropriate part of this common information.

    3. The default manager on a class is either the first manager declared on
       the class, if that exists, or the default manager of the first abstract
       base class in the parent hierarchy, if that exists. If no default
       manager is explicitly declared, Django's normal default manager is
       used.