diff options
| author | Loic Bistuer <loic.bistuer@sixmedia.com> | 2013-07-26 11:59:40 +0300 |
|---|---|---|
| committer | Anssi Kääriäinen <akaariai@gmail.com> | 2013-07-26 12:41:27 +0300 |
| commit | 31fadc120213284da76801cc7bc56e9f32d7281b (patch) | |
| tree | 534ba416b05409f03c524e1bb3ebd7dc581667b5 /docs | |
| parent | 8f3aefdec33f6cb4bdda142ff9f7fa423c0bebbd (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.txt | 15 | ||||
| -rw-r--r-- | docs/releases/1.7.txt | 7 | ||||
| -rw-r--r-- | docs/topics/db/managers.txt | 119 |
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 |
