mirror of
https://github.com/django/django.git
synced 2025-08-03 18:38:50 +00:00
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.
This commit is contained in:
parent
8f3aefdec3
commit
31fadc1202
10 changed files with 390 additions and 127 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue