# Ticket #36915 - Request for Triage Review - select\_for\_update for implicit transactions

**URL:** <https://forum.djangoproject.com/t/ticket-36915-request-for-triage-review-select-for-update-for-implicit-transactions/44307>\
**Category:** ORM\
**Created:** [February 26, 2026, 6:10pm UTC](https://forum.djangoproject.com/t/ticket-36915-request-for-triage-review-select-for-update-for-implicit-transactions/44307 "2026-02-26T18:10:18Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![MANAS225](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/manas225/32/31684_2.png) [@MANAS225](https://forum.djangoproject.com/u/MANAS225)\
**Post date:** [February 26, 2026, 6:10pm UTC](https://forum.djangoproject.com/t/ticket-36915-request-for-triage-review-select-for-update-for-implicit-transactions/44307/1 "2026-02-26T18:10:18Z")

</div>

Hi everyone,

I have been working on ticket #36915  
(Add support for select\_for\_update for implicit transactions).

The problem is that select\_for\_update() raises  
TransactionManagementError when used as a subquery  
inside .update() even though DML creates an implicit  
transaction at the database level.

I have a working proof of concept fix:  
PR: [Fixed #36915 -- Allow select\_for\_update() in subqueries of DML statements. by MANAS225 · Pull Request #20773 · django/django · GitHub](https://github.com/django/django/pull/20773)

The PR was closed because the ticket was not yet  
accepted. Could someone please review ticket #36915  
and accept it so the PR can be reconsidered?

Ticket: [#36915 (Add support for select\_for\_update for implicit transactions) – Django](https://code.djangoproject.com/ticket/36915)

Thank you!

---

<div class="post-metadata">

**Author:** ![charettes](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/charettes/32/27_2.png) [@charettes](https://forum.djangoproject.com/u/charettes)\
**Post date:** [February 26, 2026, 6:34pm UTC](https://forum.djangoproject.com/t/ticket-36915-request-for-triage-review-select-for-update-for-implicit-transactions/44307/2 "2026-02-26T18:34:28Z")

</div>

I agree with Jacob assessent that no changes are required and the docs are adequate.

The patch you’ve proposed that relies on `self.subquery` assumes that all subqueries are nested im DML statement (e.g. `UPDATE`, `DELETE`) that create implicit transactions but it’s not the case.

It would wrongly allow queries of the form

```python
Person.objects.filter(
    pk__in=Person.objects.filter(name="Reinhardt").select_for_update()
).get()

```

without an explicit wrapping in a `transaction.atomic`.

There are likely ways to adjust compilers (e.g. `SQLUpdateCompiler` and `SQLDeleteCompiler`) to denote whether or not they result in an implicit transaction but the challenge will be that we’d need to adjust the compiling logic to keep track of _parent_ compilers which is not trivial.

In other words, I don’t think implementing such feature is worth the complexity it would introduce.

---

<div class="post-metadata">

**Author:** ![charettes](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/charettes/32/27_2.png) [@charettes](https://forum.djangoproject.com/u/charettes)\
**Post date:** [February 27, 2026, 2:53pm UTC](https://forum.djangoproject.com/t/ticket-36915-request-for-triage-review-select-for-update-for-implicit-transactions/44307/3 "2026-02-27T14:53:21Z")

</div>

I was curious to see how invasive such a change would be and I figured I’d share what I’ve found

```diff
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index cfb4b93e65..1558e1cec3 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -43,6 +43,7 @@ class SQLCompiler:
         r"^(.*)\s(?:ASC|DESC).*",
         re.MULTILINE | re.DOTALL,
     )
+ in_implicit_transaction = False
 
     def __init__ (self, query, connection, using, elide_empty=True):
         self.query = query
@@ -828,7 +829,8 @@ class SQLCompiler:
 
                 if self.query.select_for_update and features.has_select_for_update:
                     if (
- self.connection.get_autocommit()
+ not self.in_implicit_transaction
+ and self.connection.get_autocommit()
                         # Don't raise an exception when database doesn't
                         # support transactions, as it's a noop.
                         and features.supports_transactions
@@ -1671,6 +1673,7 @@ class SQLCompiler:
 class SQLInsertCompiler(SQLCompiler):
     returning_fields = None
     returning_params = ()
+ in_implicit_transaction = True
 
     def field_as_sql(self, field, get_placeholder, val):
         """
@@ -1954,6 +1957,8 @@ class SQLInsertCompiler(SQLCompiler):
 
 
 class SQLDeleteCompiler(SQLCompiler):
+ in_implicit_transaction = True
+
     @cached_property
     def single_alias(self):
         # Ensure base table is in aliases.
@@ -2016,6 +2021,7 @@ class SQLDeleteCompiler(SQLCompiler):
 class SQLUpdateCompiler(SQLCompiler):
     returning_fields = None
     returning_params = ()
+ in_implicit_transaction = True
 
     def as_sql(self):
         """
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 7a4cf843c1..125f740555 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -1314,7 +1314,9 @@ class Query(BaseExpression):
             self.clear_ordering(force=False)
             for query in self.combined_queries:
                 query.clear_ordering(force=False)
- sql, params = self.get_compiler(connection=connection).as_sql()
+ query_compiler = self.get_compiler(connection=connection)
+ query_compiler.in_implicit_transaction |= compiler.in_implicit_transaction
+ sql, params = query_compiler.as_sql()
         if self.subquery:
             sql = "(%s)" % sql
         return sql, params

```

The idea is that `SQLCompiler` has a `in_implicit_transaction: bool` flag that determines whether it compiles a query meant to be executed in an implicit transaction and `sql.Query.as_sql` (called at subquery compiling time) union this flag.

This means that subqueries in DML get their flag enabled while ones that are not remain `False` (the core problem for checking `self.subquery` like the patch did).

I still remain on the fence about whether it’s worth adding but I thought I’d share how this can be achieved if it can be handy in the future.
