summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorLoic Bistuer <loic.bistuer@sixmedia.com>2013-07-26 11:59:40 +0300
committerAnssi Kääriäinen <akaariai@gmail.com>2013-07-26 12:41:27 +0300
commit31fadc120213284da76801cc7bc56e9f32d7281b (patch)
tree534ba416b05409f03c524e1bb3ebd7dc581667b5 /docs
parent8f3aefdec33f6cb4bdda142ff9f7fa423c0bebbd (diff)
Fixed #20625 -- Chainable Manager/QuerySet methods.
Additionally this patch solves the orthogonal problem that specialized `QuerySet` like `ValuesQuerySet` didn't inherit from the current `QuerySet` type. This wasn't an issue until now because we didn't officially support custom `QuerySet` but it became necessary with the introduction of this new feature. Thanks aaugustin, akaariai, carljm, charettes, mjtamlyn, shaib and timgraham for the reviews.
Diffstat (limited to 'docs')
-rw-r--r--docs/ref/models/querysets.txt15
-rw-r--r--docs/releases/1.7.txt7
-rw-r--r--docs/topics/db/managers.txt119
3 files changed, 138 insertions, 3 deletions
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index 3963785733..c3f6a660b4 100644
--- a/docs/ref/models/querysets.txt
+++ b/docs/ref/models/querysets.txt
@@ -121,9 +121,7 @@ described here.
QuerySet API
============
-Though you usually won't create one manually — you'll go through a
-:class:`~django.db.models.Manager` — here's the formal declaration of a
-``QuerySet``:
+Here's the formal declaration of a ``QuerySet``:
.. class:: QuerySet([model=None, query=None, using=None])
@@ -1866,6 +1864,17 @@ DO_NOTHING do not prevent taking the fast-path in deletion.
Note that the queries generated in object deletion is an implementation
detail subject to change.
+as_manager
+~~~~~~~~~~
+
+.. classmethod:: as_manager()
+
+.. versionadded:: 1.7
+
+Class method that returns an instance of :class:`~django.db.models.Manager`
+with a copy of the ``QuerySet``'s methods. See
+:ref:`create-manager-with-queryset-methods` for more details.
+
.. _field-lookups:
Field lookups
diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt
index bec5aaa12a..3526b2bce7 100644
--- a/docs/releases/1.7.txt
+++ b/docs/releases/1.7.txt
@@ -30,6 +30,13 @@ security support until the release of Django 1.8.
What's new in Django 1.7
========================
+Calling custom ``QuerySet`` methods from the ``Manager``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :meth:`QuerySet.as_manager() <django.db.models.query.QuerySet.as_manager>`
+class method has been added to :ref:`create Manager with QuerySet methods
+<create-manager-with-queryset-methods>`.
+
Admin shortcuts support time zones
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/topics/db/managers.txt b/docs/topics/db/managers.txt
index b940b09d33..3b83865e60 100644
--- a/docs/topics/db/managers.txt
+++ b/docs/topics/db/managers.txt
@@ -201,6 +201,125 @@ attribute on the manager class. This is documented fully below_.
.. _below: manager-types_
+.. _calling-custom-queryset-methods-from-manager:
+
+Calling custom ``QuerySet`` methods from the ``Manager``
+--------------------------------------------------------
+
+While most methods from the standard ``QuerySet`` are accessible directly from
+the ``Manager``, this is only the case for the extra methods defined on a
+custom ``QuerySet`` if you also implement them on the ``Manager``::
+
+ class PersonQuerySet(models.QuerySet):
+ def male(self):
+ return self.filter(sex='M')
+
+ def female(self):
+ return self.filter(sex='F')
+
+ class PersonManager(models.Manager):
+ def get_queryset(self):
+ return PersonQuerySet()
+
+ def male(self):
+ return self.get_queryset().male()
+
+ def female(self):
+ return self.get_queryset().female()
+
+ 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 = PersonManager()
+
+This example allows you to call both ``male()`` and ``female()`` directly from
+the manager ``Person.people``.
+
+.. _create-manager-with-queryset-methods:
+
+Creating ``Manager`` with ``QuerySet`` methods
+----------------------------------------------
+
+.. versionadded:: 1.7
+
+In lieu of the above approach which requires duplicating methods on both the
+``QuerySet`` and the ``Manager``, :meth:`QuerySet.as_manager()
+<django.db.models.query.QuerySet.as_manager>` can be used to create an instance
+of ``Manager`` with a copy of a custom ``QuerySet``'s methods::
+
+ class Person(models.Model):
+ ...
+ people = PersonQuerySet.as_manager()
+
+The ``Manager`` instance created by :meth:`QuerySet.as_manager()
+<django.db.models.query.QuerySet.as_manager>` will be virtually
+identical to the ``PersonManager`` from the previous example.
+
+Not every ``QuerySet`` method makes sense at the ``Manager`` level; for
+instance we intentionally prevent the :meth:`QuerySet.delete()
+<django.db.models.query.QuerySet.delete>` method from being copied onto
+the ``Manager`` class.
+
+Methods are copied according to the following rules:
+
+- Public methods are copied by default.
+- Private methods (starting with an underscore) are not copied by default.
+- Methods with a `queryset_only` attribute set to `False` are always copied.
+- Methods with a `queryset_only` attribute set to `True` are never copied.
+
+For example::
+
+ class CustomQuerySet(models.QuerySet):
+ # Available on both Manager and QuerySet.
+ def public_method(self):
+ return
+
+ # Available only on QuerySet.
+ def _private_method(self):
+ return
+
+ # Available only on QuerySet.
+ def opted_out_public_method(self):
+ return
+ opted_out_public_method.queryset_only = True
+
+ # Available on both Manager and QuerySet.
+ def _opted_in_private_method(self):
+ return
+ _opted_in_private_method.queryset_only = False
+
+from_queryset
+~~~~~~~~~~~~~
+
+.. classmethod:: from_queryset(queryset_class)
+
+For advance usage you might want both a custom ``Manager`` and a custom
+``QuerySet``. You can do that by calling ``Manager.from_queryset()`` which
+returns a *subclass* of your base ``Manager`` with a copy of the custom
+``QuerySet`` methods::
+
+ class BaseManager(models.Manager):
+ def __init__(self, *args, **kwargs):
+ ...
+
+ def manager_only_method(self):
+ return
+
+ class CustomQuerySet(models.QuerySet):
+ def manager_and_queryset_method(self):
+ return
+
+ class MyModel(models.Model):
+ objects = BaseManager.from_queryset(CustomQueryset)(*args, **kwargs)
+
+You may also store the generated class into a variable::
+
+ CustomManager = BaseManager.from_queryset(CustomQueryset)
+
+ class MyModel(models.Model):
+ objects = CustomManager(*args, **kwargs)
+
.. _custom-managers-and-inheritance:
Custom managers and model inheritance