diff options
Diffstat (limited to 'django/db/models/sql')
| -rw-r--r-- | django/db/models/sql/compiler.py | 39 | ||||
| -rw-r--r-- | django/db/models/sql/subqueries.py | 35 |
2 files changed, 30 insertions, 44 deletions
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 9493d0dc3c..638813333d 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -7,13 +7,16 @@ from django.core.exceptions import EmptyResultSet, FieldError from django.db.models.constants import LOOKUP_SEP from django.db.models.expressions import OrderBy, Random, RawSQL, Ref, Value from django.db.models.functions import Cast -from django.db.models.query_utils import QueryWrapper, select_related_descend +from django.db.models.query_utils import ( + Q, QueryWrapper, select_related_descend, +) from django.db.models.sql.constants import ( CURSOR, GET_ITERATOR_CHUNK_SIZE, MULTI, NO_RESULTS, ORDER_DIR, SINGLE, ) from django.db.models.sql.query import Query, get_order_dir from django.db.transaction import TransactionManagementError from django.db.utils import DatabaseError, NotSupportedError +from django.utils.functional import cached_property from django.utils.hashable import make_hashable @@ -1344,19 +1347,37 @@ class SQLInsertCompiler(SQLCompiler): class SQLDeleteCompiler(SQLCompiler): + @cached_property + def single_alias(self): + return sum(self.query.alias_refcount[t] > 0 for t in self.query.alias_map) == 1 + + def _as_sql(self, query): + result = [ + 'DELETE FROM %s' % self.quote_name_unless_alias(query.base_table) + ] + where, params = self.compile(query.where) + if where: + result.append('WHERE %s' % where) + return ' '.join(result), tuple(params) + def as_sql(self): """ Create the SQL for this query. Return the SQL string and list of parameters. """ - assert len([t for t in self.query.alias_map if self.query.alias_refcount[t] > 0]) == 1, \ - "Can only delete from one table at a time." - qn = self.quote_name_unless_alias - result = ['DELETE FROM %s' % qn(self.query.base_table)] - where, params = self.compile(self.query.where) - if where: - result.append('WHERE %s' % where) - return ' '.join(result), tuple(params) + if self.single_alias: + return self._as_sql(self.query) + innerq = self.query.clone() + innerq.__class__ = Query + innerq.clear_select_clause() + pk = self.query.model._meta.pk + innerq.select = [ + pk.get_col(self.query.get_initial_alias()) + ] + outerq = Query(self.query.model) + outerq.where = self.query.where_class() + outerq.add_q(Q(pk__in=innerq)) + return self._as_sql(outerq) class SQLUpdateCompiler(SQLCompiler): diff --git a/django/db/models/sql/subqueries.py b/django/db/models/sql/subqueries.py index fbc265d113..af48239cdc 100644 --- a/django/db/models/sql/subqueries.py +++ b/django/db/models/sql/subqueries.py @@ -3,7 +3,6 @@ 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 ( CURSOR, GET_ITERATOR_CHUNK_SIZE, NO_RESULTS, @@ -41,40 +40,6 @@ class DeleteQuery(Query): num_deleted += self.do_query(self.get_meta().db_table, self.where, using=using) return num_deleted - def delete_qs(self, query, using): - """ - Delete the queryset in one SQL query (if possible). For simple queries - this is done by copying the query.query.where to self.query, for - complex queries by using subquery. - """ - innerq = query.query - # Make sure the inner query has at least one table in use. - innerq.get_initial_alias() - # The same for our new query. - self.get_initial_alias() - innerq_used_tables = tuple([t for t in innerq.alias_map if innerq.alias_refcount[t]]) - if not innerq_used_tables or innerq_used_tables == tuple(self.alias_map): - # There is only the base table in use in the query. - self.where = innerq.where - else: - pk = query.model._meta.pk - if not connections[using].features.update_can_self_select: - # We can't do the delete using subquery. - values = list(query.values_list('pk', flat=True)) - if not values: - return 0 - return self.delete_batch(values, using) - else: - innerq.clear_select_clause() - innerq.select = [ - pk.get_col(self.get_initial_alias()) - ] - values = innerq - self.where = self.where_class() - self.add_q(Q(pk__in=values)) - cursor = self.get_compiler(using).execute_sql(CURSOR) - return cursor.rowcount if cursor else 0 - class UpdateQuery(Query): """An UPDATE SQL query.""" |
