1
0
mirror of https://github.com/django/django.git synced 2025-07-17 16:19:12 +00:00

[3.1.x] Modernized custom manager example

Since this example was added 15 years ago in a8ccdd0fcd631e8e928ef20547e1fe3e313dc607, the ORM has gained the ability to do the `COUNT(*)` related query, so do it with the ORM to avoid misleading users that raw SQL is only supported from manager methods.

Backport of 59e503b6708d41a44f2aa320272de3e2ecb5d65c from master
This commit is contained in:
Adam Johnson 2021-01-25 16:16:52 +00:00 committed by Carlton Gibson
parent 82e1294602
commit a271d8c15c

View File

@ -55,47 +55,34 @@ 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 that act on a single instance of a model object -- use :ref:`Model methods
<model-methods>`, not custom ``Manager`` methods.) <model-methods>`, not custom ``Manager`` methods.)
A custom ``Manager`` method can return anything you want. It doesn't have to For example, this custom ``Manager`` adds a method ``with_counts()``::
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::
from django.db import models from django.db import models
from django.db.models.functions import Coalesce
class PollManager(models.Manager): class PollManager(models.Manager):
def with_counts(self): def with_counts(self):
from django.db import connection return self.annotate(
with connection.cursor() as cursor: num_responses=Coalesce(models.Count("response"), 0)
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 p.id, p.question, p.poll_date
ORDER BY p.poll_date 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): class OpinionPoll(models.Model):
question = models.CharField(max_length=200) question = models.CharField(max_length=200)
poll_date = models.DateField()
objects = PollManager() objects = PollManager()
class Response(models.Model): class Response(models.Model):
poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE) poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
person_name = models.CharField(max_length=50) # ...
response = models.TextField()
With this example, you'd use ``OpinionPoll.objects.with_counts()`` to return With this example, you'd use ``OpinionPoll.objects.with_counts()`` to get a
that list of ``OpinionPoll`` objects with ``num_responses`` attributes. ``QuerySet`` of ``OpinionPoll`` objects with the extra ``num_responses``
attribute attached.
Another thing to note about this example is that ``Manager`` methods can A custom ``Manager`` method can return anything you want. It doesn't have to
access ``self.model`` to get the model class to which they're attached. return a ``QuerySet``.
Another thing to note is that ``Manager`` methods can access ``self.model`` to
get the model class to which they're attached.
Modifying a manager's initial ``QuerySet`` Modifying a manager's initial ``QuerySet``
------------------------------------------ ------------------------------------------