11from __future__ import annotations
22
3- from collections import defaultdict
43import types
4+ from collections import defaultdict
55from collections .abc import AsyncIterator , Callable , Collection , Generator , Iterable
66from copy import copy
77from typing import TYPE_CHECKING , Any , Generic , Optional , TypeVar , cast , overload
@@ -326,6 +326,7 @@ class QuerySet(AwaitableQuery[MODEL]):
326326 "_select_for_update_nowait" ,
327327 "_select_for_update_skip_locked" ,
328328 "_select_for_update_of" ,
329+ "_select_for_update_no_key" ,
329330 "_select_related" ,
330331 "_select_related_idx" ,
331332 "_use_indexes" ,
@@ -351,6 +352,7 @@ def __init__(self, model: type[MODEL]) -> None:
351352 self ._select_for_update_nowait : bool = False
352353 self ._select_for_update_skip_locked : bool = False
353354 self ._select_for_update_of : set [str ] = set ()
355+ self ._select_for_update_no_key : bool = False
354356 self ._select_related : set [str ] = set ()
355357 self ._select_related_idx : list [
356358 tuple [type [Model ], int , Table | str , type [Model ], Iterable [str | None ]]
@@ -385,6 +387,7 @@ def _clone(self) -> QuerySet[MODEL]:
385387 queryset ._select_for_update_nowait = self ._select_for_update_nowait
386388 queryset ._select_for_update_skip_locked = self ._select_for_update_skip_locked
387389 queryset ._select_for_update_of = self ._select_for_update_of
390+ queryset ._select_for_update_no_key = self ._select_for_update_no_key
388391 queryset ._select_related = self ._select_related
389392 queryset ._select_related_idx = self ._select_related_idx
390393 queryset ._force_indexes = self ._force_indexes
@@ -572,20 +575,40 @@ def distinct(self) -> QuerySet[MODEL]:
572575 return queryset
573576
574577 def select_for_update (
575- self , nowait : bool = False , skip_locked : bool = False , of : tuple [str , ...] = ()
578+ self ,
579+ nowait : bool = False ,
580+ skip_locked : bool = False ,
581+ of : tuple [str , ...] = (),
582+ no_key : bool = False ,
576583 ) -> QuerySet [MODEL ]:
577584 """
578585 Make QuerySet select for update.
579586
580587 Returns a queryset that will lock rows until the end of the transaction,
581588 generating a SELECT ... FOR UPDATE SQL statement on supported databases.
589+
590+ :param nowait:
591+ If `True`, raise an error if the lock cannot be obtained immediately.
592+ :param skip_locked:
593+ If `True`, skip rows that are already locked by other transactions instead of waiting.
594+ :param of:
595+ Specify the tables to lock when dealing with multiple related tables, e.g. when using `select_related`.
596+ Provide a tuple of table names to indicate which tables' rows should be locked. By default, all fetched
597+ rows are locked.
598+ :param no_key:
599+ If `True`, use the lower SELECT ... FOR NO KEY UPDATE lock strength on PostgreSQL to allow creating or
600+ deleting rows in other tables that reference the locked rows via foreign keys. The parameter is ignored
601+ on other backends.
582602 """
583603 if self .capabilities .support_for_update :
584604 queryset = self ._clone ()
585605 queryset ._select_for_update = True
586606 queryset ._select_for_update_nowait = nowait
587607 queryset ._select_for_update_skip_locked = skip_locked
588608 queryset ._select_for_update_of = set (of )
609+ queryset ._select_for_update_no_key = (
610+ no_key and self .capabilities .support_for_no_key_update
611+ )
589612 return queryset
590613 return self
591614
@@ -1186,6 +1209,7 @@ def _make_query(self) -> None:
11861209 self ._select_for_update_nowait ,
11871210 self ._select_for_update_skip_locked ,
11881211 self ._select_for_update_of ,
1212+ self ._select_for_update_no_key ,
11891213 )
11901214 if self ._select_related :
11911215 for select_related in self ._select_related :
0 commit comments