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
|
b
|
True
|
51 | 51 | # Submitting an invalid session key (either by guessing, or if the db has |
52 | 52 | # removed the key) results in a new key being generated. |
53 | 53 | >>> Session.objects.filter(pk=db_session.session_key).delete() |
| 54 | 1 |
54 | 55 | >>> db_session = DatabaseSession(db_session.session_key) |
55 | 56 | >>> db_session.save() |
56 | 57 | >>> DatabaseSession('1').get('cat') |
… |
… |
False
|
126 | 127 | True |
127 | 128 | |
128 | 129 | >>> Session.objects.filter(pk=file_session.session_key).delete() |
| 130 | 0 |
129 | 131 | >>> file_session = FileSession(file_session.session_key) |
130 | 132 | >>> file_session.save() |
131 | 133 | |
… |
… |
True
|
189 | 191 | True |
190 | 192 | |
191 | 193 | >>> Session.objects.filter(pk=cache_session.session_key).delete() |
| 194 | 0 |
192 | 195 | >>> cache_session = CacheSession(cache_session.session_key) |
193 | 196 | >>> cache_session.save() |
194 | 197 | >>> 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
|
b
|
class QuerySet(object):
|
428 | 428 | # Delete objects in chunks to prevent the list of related objects from |
429 | 429 | # becoming too long. |
430 | 430 | seen_objs = None |
| 431 | count = 0 |
431 | 432 | while 1: |
432 | 433 | # Collect all the objects to be deleted in this chunk, and all the |
433 | 434 | # objects that are related to the objects that are to be deleted. |
434 | 435 | seen_objs = CollectedObjects(seen_objs) |
435 | 436 | for object in del_query[:CHUNK_SIZE]: |
436 | 437 | object._collect_sub_objects(seen_objs) |
| 438 | count += 1 |
437 | 439 | |
438 | 440 | if not seen_objs: |
439 | 441 | break |
… |
… |
class QuerySet(object):
|
441 | 443 | |
442 | 444 | # Clear the result cache, in case this QuerySet gets reused. |
443 | 445 | self._result_cache = None |
| 446 | return count |
444 | 447 | delete.alters_data = True |
445 | 448 | |
446 | 449 | def update(self, **kwargs): |
diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt
index 286a0c4..7389531 100644
a
|
b
|
deletes the object and has no return value. Example::
|
729 | 729 | e.delete() |
730 | 730 | |
731 | 731 | You can also delete objects in bulk. Every ``QuerySet`` has a ``delete()`` |
732 | | method, which deletes all members of that ``QuerySet``. |
| 732 | method, which deletes all members of that ``QuerySet`` and returns the number of |
| 733 | items deleted. |
733 | 734 | |
734 | 735 | For example, this deletes all ``Entry`` objects with a ``pub_date`` year of |
735 | 736 | 2005:: |
736 | 737 | |
737 | | Entry.objects.filter(pub_date__year=2005).delete() |
| 738 | >>> Entry.objects.filter(pub_date__year=2005).delete() |
| 739 | 42 |
738 | 740 | |
739 | 741 | Keep in mind that this will, whenever possible, be executed purely in |
740 | 742 | 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
|
b
|
AttributeError: Manager isn't accessible via Article instances
|
365 | 365 | >>> Article.objects.all() |
366 | 366 | [<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>] |
367 | 367 | >>> Article.objects.filter(id__lte=4).delete() |
| 368 | 4 |
368 | 369 | >>> Article.objects.all() |
369 | 370 | [<Article: Article 6>, <Article: Default headline>, <Article: Article 7>, <Article: Updated article 8>] |
370 | 371 | """} |
diff --git a/tests/modeltests/generic_relations/models.py b/tests/modeltests/generic_relations/models.py
index 1bc6577..075eab0 100644
a
|
b
|
__test__ = {'API_TESTS':"""
|
167 | 167 | [(u'clearish', <ContentType: mineral>, 1), (u'fatty', <ContentType: animal>, 1), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 1)] |
168 | 168 | |
169 | 169 | >>> TaggedItem.objects.filter(tag='fatty').delete() |
170 | | |
| 170 | 1 |
171 | 171 | >>> ctype = ContentType.objects.get_for_model(lion) |
172 | 172 | >>> Animal.objects.filter(tags__content_type=ctype) |
173 | 173 | [<Animal: Platypus>] |
… |
… |
__test__ = {'API_TESTS':"""
|
197 | 197 | # Filtering and deleting works |
198 | 198 | >>> subjective = ["cooler"] |
199 | 199 | >>> tiger.comparisons.filter(comparative__in=subjective).delete() |
| 200 | 2 |
200 | 201 | >>> Comparison.objects.all() |
201 | 202 | [<Comparison: cheetah is faster than tiger>, <Comparison: tiger is stronger than cheetah>] |
202 | 203 | |
diff --git a/tests/modeltests/many_to_many/models.py b/tests/modeltests/many_to_many/models.py
index 21b4e82..a4a9c8d 100644
a
|
b
|
TypeError: 'Publication' instance expected
|
243 | 243 | |
244 | 244 | # Bulk delete some Publications - references to deleted publications should go |
245 | 245 | >>> Publication.objects.filter(title__startswith='Science').delete() |
| 246 | 2 |
246 | 247 | >>> Publication.objects.all() |
247 | 248 | [<Publication: Highlights for Children>, <Publication: The Python Journal>] |
248 | 249 | >>> Article.objects.all() |
… |
… |
TypeError: 'Publication' instance expected
|
255 | 256 | >>> print q |
256 | 257 | [<Article: Django lets you build Web apps easily>] |
257 | 258 | >>> q.delete() |
| 259 | 1 |
258 | 260 | |
259 | 261 | # After the delete, the QuerySet cache needs to be cleared, and the referenced objects should be gone |
260 | 262 | >>> 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
|
b
|
True
|
292 | 292 | |
293 | 293 | # You can delete using a JOIN in the query. |
294 | 294 | >>> Reporter.objects.filter(article__headline__startswith='This').delete() |
| 295 | 3 |
295 | 296 | >>> Reporter.objects.all() |
296 | 297 | [] |
297 | 298 | >>> Article.objects.all() |
diff --git a/tests/modeltests/proxy_models/models.py b/tests/modeltests/proxy_models/models.py
index 28446b9..b2e4e8f 100644
a
|
b
|
FieldError: Proxy model 'NoNewFields' contains model fields.
|
264 | 264 | # Manager tests. |
265 | 265 | |
266 | 266 | >>> Person.objects.all().delete() |
| 267 | 5 |
267 | 268 | >>> _ = Person.objects.create(name="fred") |
268 | 269 | >>> _ = Person.objects.create(name="wilma") |
269 | 270 | >>> _ = 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
|
b
|
instances. This is a regression test for #8990, #9527
|
56 | 56 | Deleting related objects should also not be distracted by a restricted manager |
57 | 57 | on the related object. This is a regression test for #2698. |
58 | 58 | >>> RestrictedModel.plain_manager.all().delete() |
| 59 | 1 |
59 | 60 | >>> for name, public in (('one', True), ('two', False), ('three', False)): |
60 | 61 | ... _ = RestrictedModel.objects.create(name=name, is_public=public, related=related) |
61 | 62 | |
diff --git a/tests/regressiontests/m2m_through_regress/models.py b/tests/regressiontests/m2m_through_regress/models.py
index 56aecd6..0c25775 100644
a
|
b
|
AttributeError: Cannot use create() on a ManyToManyField which specifies an inte
|
126 | 126 | # one for each end of the m2m, plus the through model. |
127 | 127 | |
128 | 128 | >>> User.objects.all().delete() |
| 129 | 3 |
129 | 130 | >>> UserMembership.objects.all().delete() |
| 131 | 0 |
130 | 132 | >>> frank.delete() |
131 | 133 | >>> rock.delete() |
132 | 134 | >>> 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
|
b
|
True
|
239 | 239 | |
240 | 240 | # This should delete both Restuarants, plus the related places, plus the ItalianRestaurant. |
241 | 241 | >>> Restaurant.objects.all().delete() |
242 | | |
| 242 | 2 |
243 | 243 | >>> Place.objects.get(pk=ident) |
244 | 244 | Traceback (most recent call last): |
245 | 245 | ... |
diff --git a/tests/regressiontests/model_regress/models.py b/tests/regressiontests/model_regress/models.py
index 420f2c2..2947841 100644
a
|
b
|
if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] not in (
|
159 | 159 | # The idea is that all these creations and saving should work without crashing. |
160 | 160 | # It's not rocket science. |
161 | 161 | >>> Article.objects.all().delete() |
| 162 | 2 |
162 | 163 | >>> dt1 = datetime.datetime(2008, 8, 31, 16, 20, tzinfo=tzinfo.FixedOffset(600)) |
163 | 164 | >>> dt2 = datetime.datetime(2008, 8, 31, 17, 20, tzinfo=tzinfo.FixedOffset(600)) |
164 | 165 | >>> obj = Article.objects.create(headline="A headline", pub_date=dt1, article_text="foo") |