1
1
from __future__ import annotations
2
2
3
- from collections import defaultdict
4
3
import types
4
+ from collections import defaultdict
5
5
from collections .abc import AsyncIterator , Callable , Collection , Generator , Iterable
6
6
from copy import copy
7
7
from typing import TYPE_CHECKING , Any , Generic , Optional , TypeVar , cast , overload
@@ -326,6 +326,7 @@ class QuerySet(AwaitableQuery[MODEL]):
326
326
"_select_for_update_nowait" ,
327
327
"_select_for_update_skip_locked" ,
328
328
"_select_for_update_of" ,
329
+ "_select_for_update_no_key" ,
329
330
"_select_related" ,
330
331
"_select_related_idx" ,
331
332
"_use_indexes" ,
@@ -351,6 +352,7 @@ def __init__(self, model: type[MODEL]) -> None:
351
352
self ._select_for_update_nowait : bool = False
352
353
self ._select_for_update_skip_locked : bool = False
353
354
self ._select_for_update_of : set [str ] = set ()
355
+ self ._select_for_update_no_key : bool = False
354
356
self ._select_related : set [str ] = set ()
355
357
self ._select_related_idx : list [
356
358
tuple [type [Model ], int , Table | str , type [Model ], Iterable [str | None ]]
@@ -385,6 +387,7 @@ def _clone(self) -> QuerySet[MODEL]:
385
387
queryset ._select_for_update_nowait = self ._select_for_update_nowait
386
388
queryset ._select_for_update_skip_locked = self ._select_for_update_skip_locked
387
389
queryset ._select_for_update_of = self ._select_for_update_of
390
+ queryset ._select_for_update_no_key = self ._select_for_update_no_key
388
391
queryset ._select_related = self ._select_related
389
392
queryset ._select_related_idx = self ._select_related_idx
390
393
queryset ._force_indexes = self ._force_indexes
@@ -572,20 +575,40 @@ def distinct(self) -> QuerySet[MODEL]:
572
575
return queryset
573
576
574
577
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 ,
576
583
) -> QuerySet [MODEL ]:
577
584
"""
578
585
Make QuerySet select for update.
579
586
580
587
Returns a queryset that will lock rows until the end of the transaction,
581
588
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.
582
602
"""
583
603
if self .capabilities .support_for_update :
584
604
queryset = self ._clone ()
585
605
queryset ._select_for_update = True
586
606
queryset ._select_for_update_nowait = nowait
587
607
queryset ._select_for_update_skip_locked = skip_locked
588
608
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
+ )
589
612
return queryset
590
613
return self
591
614
@@ -1186,6 +1209,7 @@ def _make_query(self) -> None:
1186
1209
self ._select_for_update_nowait ,
1187
1210
self ._select_for_update_skip_locked ,
1188
1211
self ._select_for_update_of ,
1212
+ self ._select_for_update_no_key ,
1189
1213
)
1190
1214
if self ._select_related :
1191
1215
for select_related in self ._select_related :
0 commit comments