Opened 6 weeks ago

Closed 6 weeks ago

#28682 closed New feature (wontfix)

Allow QuerySet.update() to return the IDs of the matched rows

Reported by: Дилян Палаузов Owned by: nobody
Component: Database layer (models, ORM) Version: 1.11
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Дилян Палаузов)

By coincidence all backends that can_return_ids_from_bulk_insert, which happens to be only Postgresql, can also return ids from UPDATE:

UPDATE ... SET ... RETURNING id;

  • update QuerySet.update() to return the PKs of the updated rows
  • update the documentation of QuerySet.update(), stating the specific return type for PG
  • possibly rename can_return_ids_from_bulk_insert to can_return_ids or can_return_anything(_from_select_insert_update_delete)?

Like this:

diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -1230,11 +1230,11 @@ class SQLInsertCompiler(SQLCompiler):
             else:
                 result.append("VALUES (%s)" % ", ".join(placeholder_rows[0]))
                 params = [param_rows[0]]
-            col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column))
             r_fmt, r_params = self.connection.ops.return_insert_id()
             # Skip empty r_fmt to allow subclasses to customize behavior for
             # 3rd party backends. Refs #19096.
             if r_fmt:
+                col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column))
                 result.append(r_fmt % col)
                 params += [r_params]
             return [(" ".join(result), tuple(chain.from_iterable(params)))]
@@ -1341,6 +1341,9 @@ class SQLUpdateCompiler(SQLCompiler):
         where, params = self.compile(self.query.where)
         if where:
             result.append('WHERE %s' % where)
+        if self.connection.features.can_return_ids_from_bulk_insert:
+            opts = self.query.get_meta()
+            result.append("RETURNING %s.%s" % (qn(opts.db_table), qn(opts.pk.column)))
         return ' '.join(result), tuple(update_params + params)
 
     def execute_sql(self, result_type):
@@ -1352,6 +1355,8 @@ class SQLUpdateCompiler(SQLCompiler):
         """
         cursor = super().execute_sql(result_type)
         try:
+            if self.connection.features.can_return_ids_from_bulk_insert: # for Postgresql return the
+                return self.connection.ops.fetch_returned_insert_ids(cursor) # PKs of the matched columns
             rows = cursor.rowcount if cursor else 0
             is_empty = cursor is None
         finally:
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
--- a/docs/ref/models/querysets.txt
+++ b/docs/ref/models/querysets.txt
@@ -2241,7 +2241,8 @@ retrieves the results and then checks if any were returned.
 
 Performs an SQL update query for the specified fields, and returns
 the number of rows matched (which may not be equal to the number of rows
-updated if some rows already have the new value).
+updated if some rows already have the new value).  On Postgresql it returns
+instead a list with the primary keys of the matched rows.
 
 For example, to turn comments off for all blog entries published in 2010,
 you could do this::

Change History (2)

comment:1 Changed 6 weeks ago by Дилян Палаузов

Description: modified (diff)

comment:2 Changed 6 weeks ago by Tim Graham

Resolution: wontfix
Status: newclosed
Summary: QuerySet.update() : return the IDs of the matched rowsAllow QuerySet.update() to return the IDs of the matched rows
Type: UncategorizedNew feature

We can't change the return type of QuerySet.update() as proposed -- that would be backwards incompatible and it would make writing code that works across different databases more difficult. Perhaps #21461 would solve your use case.

Note: See TracTickets for help on using tickets.
Back to Top