From 61aadb890f7e7bb41cf2df35d25d32d6e8ae4429 Mon Sep 17 00:00:00 2001
From: Derek Willis <dwillis@gmail.com>
Date: Mon, 22 Mar 2010 22:01:15 -0400
Subject: [PATCH] QuerySet.delete() returns number deleted

---
 django/contrib/sessions/tests.py                   |    3 +++
 django/db/models/query.py                          |    3 +++
 docs/topics/db/queries.txt                         |    6 ++++--
 tests/modeltests/basic/models.py                   |    1 +
 tests/modeltests/generic_relations/models.py       |    3 ++-
 tests/modeltests/many_to_many/models.py            |    2 ++
 tests/modeltests/many_to_one/models.py             |    1 +
 tests/modeltests/proxy_models/models.py            |    1 +
 .../custom_managers_regress/models.py              |    1 +
 .../regressiontests/m2m_through_regress/models.py  |    2 ++
 .../model_inheritance_regress/models.py            |    2 +-
 tests/regressiontests/model_regress/models.py      |    1 +
 12 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/django/contrib/sessions/tests.py b/django/contrib/sessions/tests.py
index f0a3c4e..6dbf676 100644
--- a/django/contrib/sessions/tests.py
+++ b/django/contrib/sessions/tests.py
@@ -51,6 +51,7 @@ True
 # Submitting an invalid session key (either by guessing, or if the db has
 # removed the key) results in a new key being generated.
 >>> Session.objects.filter(pk=db_session.session_key).delete()
+1
 >>> db_session = DatabaseSession(db_session.session_key)
 >>> db_session.save()
 >>> DatabaseSession('1').get('cat')
@@ -126,6 +127,7 @@ False
 True
 
 >>> Session.objects.filter(pk=file_session.session_key).delete()
+0
 >>> file_session = FileSession(file_session.session_key)
 >>> file_session.save()
 
@@ -189,6 +191,7 @@ True
 True
 
 >>> Session.objects.filter(pk=cache_session.session_key).delete()
+0
 >>> cache_session = CacheSession(cache_session.session_key)
 >>> cache_session.save()
 >>> cache_session.delete(cache_session.session_key)
diff --git a/django/db/models/query.py b/django/db/models/query.py
index f6b4419..c123cfd 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -428,12 +428,14 @@ class QuerySet(object):
         # Delete objects in chunks to prevent the list of related objects from
         # becoming too long.
         seen_objs = None
+        count = 0
         while 1:
             # Collect all the objects to be deleted in this chunk, and all the
             # objects that are related to the objects that are to be deleted.
             seen_objs = CollectedObjects(seen_objs)
             for object in del_query[:CHUNK_SIZE]:
                 object._collect_sub_objects(seen_objs)
+                count += 1
 
             if not seen_objs:
                 break
@@ -441,6 +443,7 @@ class QuerySet(object):
 
         # Clear the result cache, in case this QuerySet gets reused.
         self._result_cache = None
+        return count
     delete.alters_data = True
 
     def update(self, **kwargs):
diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt
index 286a0c4..7389531 100644
--- a/docs/topics/db/queries.txt
+++ b/docs/topics/db/queries.txt
@@ -729,12 +729,14 @@ deletes the object and has no return value. Example::
     e.delete()
 
 You can also delete objects in bulk. Every ``QuerySet`` has a ``delete()``
-method, which deletes all members of that ``QuerySet``.
+method, which deletes all members of that ``QuerySet`` and returns the number of
+items deleted.
 
 For example, this deletes all ``Entry`` objects with a ``pub_date`` year of
 2005::
 
-    Entry.objects.filter(pub_date__year=2005).delete()
+    >>> Entry.objects.filter(pub_date__year=2005).delete()
+    42
 
 Keep in mind that this will, whenever possible, be executed purely in
 SQL, and so the ``delete()`` methods of individual object instances
diff --git a/tests/modeltests/basic/models.py b/tests/modeltests/basic/models.py
index c86cb3a..f278757 100644
--- a/tests/modeltests/basic/models.py
+++ b/tests/modeltests/basic/models.py
@@ -365,6 +365,7 @@ AttributeError: Manager isn't accessible via Article instances
 >>> Article.objects.all()
 [<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Article 6>, <Article: Default headline>, <Article: Fourth article>, <Article: Article 7>, <Article: Updated article 8>]
 >>> Article.objects.filter(id__lte=4).delete()
+4
 >>> Article.objects.all()
 [<Article: Article 6>, <Article: Default headline>, <Article: Article 7>, <Article: Updated article 8>]
 """}
diff --git a/tests/modeltests/generic_relations/models.py b/tests/modeltests/generic_relations/models.py
index 1bc6577..075eab0 100644
--- a/tests/modeltests/generic_relations/models.py
+++ b/tests/modeltests/generic_relations/models.py
@@ -167,7 +167,7 @@ __test__ = {'API_TESTS':"""
 [(u'clearish', <ContentType: mineral>, 1), (u'fatty', <ContentType: animal>, 1), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 1)]
 
 >>> TaggedItem.objects.filter(tag='fatty').delete()
-
+1
 >>> ctype = ContentType.objects.get_for_model(lion)
 >>> Animal.objects.filter(tags__content_type=ctype)
 [<Animal: Platypus>]
@@ -197,6 +197,7 @@ __test__ = {'API_TESTS':"""
 # Filtering and deleting works
 >>> subjective = ["cooler"]
 >>> tiger.comparisons.filter(comparative__in=subjective).delete()
+2
 >>> Comparison.objects.all()
 [<Comparison: cheetah is faster than tiger>, <Comparison: tiger is stronger than cheetah>]
 
diff --git a/tests/modeltests/many_to_many/models.py b/tests/modeltests/many_to_many/models.py
index 21b4e82..a4a9c8d 100644
--- a/tests/modeltests/many_to_many/models.py
+++ b/tests/modeltests/many_to_many/models.py
@@ -243,6 +243,7 @@ TypeError: 'Publication' instance expected
 
 # Bulk delete some Publications - references to deleted publications should go
 >>> Publication.objects.filter(title__startswith='Science').delete()
+2
 >>> Publication.objects.all()
 [<Publication: Highlights for Children>, <Publication: The Python Journal>]
 >>> Article.objects.all()
@@ -255,6 +256,7 @@ TypeError: 'Publication' instance expected
 >>> print q
 [<Article: Django lets you build Web apps easily>]
 >>> q.delete()
+1
 
 # After the delete, the QuerySet cache needs to be cleared, and the referenced objects should be gone
 >>> print q
diff --git a/tests/modeltests/many_to_one/models.py b/tests/modeltests/many_to_one/models.py
index c6be981..a64dfd3 100644
--- a/tests/modeltests/many_to_one/models.py
+++ b/tests/modeltests/many_to_one/models.py
@@ -292,6 +292,7 @@ True
 
 # You can delete using a JOIN in the query.
 >>> Reporter.objects.filter(article__headline__startswith='This').delete()
+3
 >>> Reporter.objects.all()
 []
 >>> Article.objects.all()
diff --git a/tests/modeltests/proxy_models/models.py b/tests/modeltests/proxy_models/models.py
index 28446b9..b2e4e8f 100644
--- a/tests/modeltests/proxy_models/models.py
+++ b/tests/modeltests/proxy_models/models.py
@@ -264,6 +264,7 @@ FieldError: Proxy model 'NoNewFields' contains model fields.
 # Manager tests.
 
 >>> Person.objects.all().delete()
+5
 >>> _ = Person.objects.create(name="fred")
 >>> _ = Person.objects.create(name="wilma")
 >>> _ = Person.objects.create(name="barney")
diff --git a/tests/regressiontests/custom_managers_regress/models.py b/tests/regressiontests/custom_managers_regress/models.py
index 898730e..e30eec1 100644
--- a/tests/regressiontests/custom_managers_regress/models.py
+++ b/tests/regressiontests/custom_managers_regress/models.py
@@ -56,6 +56,7 @@ instances. This is a regression test for #8990, #9527
 Deleting related objects should also not be distracted by a restricted manager
 on the related object. This is a regression test for #2698.
 >>> RestrictedModel.plain_manager.all().delete()
+1
 >>> for name, public in (('one', True), ('two', False), ('three', False)):
 ...     _ = RestrictedModel.objects.create(name=name, is_public=public, related=related)
 
diff --git a/tests/regressiontests/m2m_through_regress/models.py b/tests/regressiontests/m2m_through_regress/models.py
index 56aecd6..0c25775 100644
--- a/tests/regressiontests/m2m_through_regress/models.py
+++ b/tests/regressiontests/m2m_through_regress/models.py
@@ -126,7 +126,9 @@ AttributeError: Cannot use create() on a ManyToManyField which specifies an inte
 # one for each end of the m2m, plus the through model.
 
 >>> User.objects.all().delete()
+3
 >>> UserMembership.objects.all().delete()
+0
 >>> frank.delete()
 >>> rock.delete()
 >>> jim.delete()
diff --git a/tests/regressiontests/model_inheritance_regress/models.py b/tests/regressiontests/model_inheritance_regress/models.py
index c669b23..a2f3f1b 100644
--- a/tests/regressiontests/model_inheritance_regress/models.py
+++ b/tests/regressiontests/model_inheritance_regress/models.py
@@ -239,7 +239,7 @@ True
 
 # This should delete both Restuarants, plus the related places, plus the ItalianRestaurant.
 >>> Restaurant.objects.all().delete()
-
+2
 >>> Place.objects.get(pk=ident)
 Traceback (most recent call last):
 ...
diff --git a/tests/regressiontests/model_regress/models.py b/tests/regressiontests/model_regress/models.py
index 420f2c2..2947841 100644
--- a/tests/regressiontests/model_regress/models.py
+++ b/tests/regressiontests/model_regress/models.py
@@ -159,6 +159,7 @@ if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] not in (
 # The idea is that all these creations and saving should work without crashing.
 # It's not rocket science.
 >>> Article.objects.all().delete()
+2
 >>> dt1 = datetime.datetime(2008, 8, 31, 16, 20, tzinfo=tzinfo.FixedOffset(600))
 >>> dt2 = datetime.datetime(2008, 8, 31, 17, 20, tzinfo=tzinfo.FixedOffset(600))
 >>> obj = Article.objects.create(headline="A headline", pub_date=dt1, article_text="foo")
-- 
1.6.5.7

