Commit 04e8d890 authored by Alexander Sosnovskiy's avatar Alexander Sosnovskiy Committed by Tim Graham
Browse files

Fixed #16891 -- Made Model/QuerySet.delete() return the number of deleted objects.

parent 9c8a2ab8
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -833,7 +833,7 @@ class Model(six.with_metaclass(ModelBase)):

        collector = Collector(using=using)
        collector.collect([self], keep_parents=keep_parents)
        collector.delete()
        return collector.delete()

    delete.alters_data = True

+8 −3
Original line number Diff line number Diff line
from collections import OrderedDict
from collections import Counter, OrderedDict
from itertools import chain
from operator import attrgetter

@@ -280,6 +280,8 @@ class Collector(object):
        # don't support transactions or cannot defer constraint checks until the
        # end of a transaction.
        self.sort()
        # number of objects deleted for each model label
        deleted_counter = Counter()

        with transaction.atomic(using=self.using, savepoint=False):
            # send pre_delete signals
@@ -291,7 +293,8 @@ class Collector(object):

            # fast deletes
            for qs in self.fast_deletes:
                qs._raw_delete(using=self.using)
                count = qs._raw_delete(using=self.using)
                deleted_counter[qs.model._meta.label] += count

            # update fields
            for model, instances_for_fieldvalues in six.iteritems(self.field_updates):
@@ -308,7 +311,8 @@ class Collector(object):
            for model, instances in six.iteritems(self.data):
                query = sql.DeleteQuery(model)
                pk_list = [obj.pk for obj in instances]
                query.delete_batch(pk_list, self.using)
                count = query.delete_batch(pk_list, self.using)
                deleted_counter[model._meta.label] += count

                if not model._meta.auto_created:
                    for obj in instances:
@@ -324,3 +328,4 @@ class Collector(object):
        for model, instances in six.iteritems(self.data):
            for instance in instances:
                setattr(instance, model._meta.pk.attname, None)
        return sum(deleted_counter.values()), dict(deleted_counter)
+4 −2
Original line number Diff line number Diff line
@@ -590,10 +590,12 @@ class QuerySet(object):

        collector = Collector(using=del_query.db)
        collector.collect(del_query)
        collector.delete()
        deleted, _rows_count = collector.delete()

        # Clear the result cache, in case this QuerySet gets reused.
        self._result_cache = None
        return deleted, _rows_count

    delete.alters_data = True
    delete.queryset_only = True

@@ -602,7 +604,7 @@ class QuerySet(object):
        Deletes objects found from the given queryset in single direct SQL
        query. No signals are sent, and there is no protection for cascades.
        """
        sql.DeleteQuery(self.model).delete_qs(self, using)
        return sql.DeleteQuery(self.model).delete_qs(self, using)
    _raw_delete.alters_data = True

    def update(self, **kwargs):
+12 −6
Original line number Diff line number Diff line
@@ -5,7 +5,9 @@ Query subclasses which provide extra functionality beyond simple data retrieval.
from django.core.exceptions import FieldError
from django.db import connections
from django.db.models.query_utils import Q
from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE, NO_RESULTS
from django.db.models.sql.constants import (
    CURSOR, GET_ITERATOR_CHUNK_SIZE, NO_RESULTS,
)
from django.db.models.sql.query import Query
from django.utils import six

@@ -23,7 +25,8 @@ class DeleteQuery(Query):
    def do_query(self, table, where, using):
        self.tables = [table]
        self.where = where
        self.get_compiler(using).execute_sql(NO_RESULTS)
        cursor = self.get_compiler(using).execute_sql(CURSOR)
        return cursor.rowcount if cursor else 0

    def delete_batch(self, pk_list, using, field=None):
        """
@@ -32,13 +35,16 @@ class DeleteQuery(Query):
        More than one physical query may be executed if there are a
        lot of values in pk_list.
        """
        # number of objects deleted
        num_deleted = 0
        if not field:
            field = self.get_meta().pk
        for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
            self.where = self.where_class()
            self.add_q(Q(
                **{field.attname + '__in': pk_list[offset:offset + GET_ITERATOR_CHUNK_SIZE]}))
            self.do_query(self.get_meta().db_table, self.where, using=using)
            num_deleted += self.do_query(self.get_meta().db_table, self.where, using=using)
        return num_deleted

    def delete_qs(self, query, using):
        """
@@ -63,8 +69,7 @@ class DeleteQuery(Query):
                values = list(query.values_list('pk', flat=True))
                if not values:
                    return
                self.delete_batch(values, using)
                return
                return self.delete_batch(values, using)
            else:
                innerq.clear_select_clause()
                innerq.select = [
@@ -73,7 +78,8 @@ class DeleteQuery(Query):
                values = innerq
            self.where = self.where_class()
            self.add_q(Q(pk__in=values))
        self.get_compiler(using).execute_sql(NO_RESULTS)
        cursor = self.get_compiler(using).execute_sql(CURSOR)
        return cursor.rowcount if cursor else 0


class UpdateQuery(Query):
+6 −1
Original line number Diff line number Diff line
@@ -537,7 +537,8 @@ Deleting objects

Issues an SQL ``DELETE`` for the object. This only deletes the object in the
database; the Python instance will still exist and will still have data in
its fields.
its fields. This method returns the number of objects deleted and a dictionary
with the number of deletions per object type.

For more details, including how to delete objects in bulk, see
:ref:`topics-db-queries-delete`.
@@ -553,6 +554,10 @@ keep the parent model's data.

    The ``keep_parents`` parameter was added.

.. versionchanged:: 1.9

    The return value describing the number of objects deleted was added.

Pickling objects
================

Loading