plain.models 0.46.1__py3-none-any.whl → 0.47.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plain/models/CHANGELOG.md +10 -0
- plain/models/deletion.py +1 -1
- plain/models/fields/related_managers.py +6 -4
- plain/models/query.py +102 -97
- {plain_models-0.46.1.dist-info → plain_models-0.47.0.dist-info}/METADATA +1 -1
- {plain_models-0.46.1.dist-info → plain_models-0.47.0.dist-info}/RECORD +9 -9
- {plain_models-0.46.1.dist-info → plain_models-0.47.0.dist-info}/WHEEL +0 -0
- {plain_models-0.46.1.dist-info → plain_models-0.47.0.dist-info}/entry_points.txt +0 -0
- {plain_models-0.46.1.dist-info → plain_models-0.47.0.dist-info}/licenses/LICENSE +0 -0
plain/models/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# plain-models changelog
|
2
2
|
|
3
|
+
## [0.47.0](https://github.com/dropseed/plain/releases/plain-models@0.47.0) (2025-09-25)
|
4
|
+
|
5
|
+
### What's changed
|
6
|
+
|
7
|
+
- The `QuerySet.query` property has been renamed to `QuerySet.sql_query` to better distinguish it from the `Model.query` manager interface ([d250eea](https://github.com/dropseed/plain/commit/d250eeac03))
|
8
|
+
|
9
|
+
### Upgrade instructions
|
10
|
+
|
11
|
+
- If you directly accessed the `QuerySet.query` property in your code (typically for advanced query manipulation or debugging), rename it to `QuerySet.sql_query`
|
12
|
+
|
3
13
|
## [0.46.1](https://github.com/dropseed/plain/releases/plain-models@0.46.1) (2025-09-25)
|
4
14
|
|
5
15
|
### What's changed
|
plain/models/deletion.py
CHANGED
@@ -288,7 +288,7 @@ class Collector:
|
|
288
288
|
# relationships are select_related as interactions between both
|
289
289
|
# features are hard to get right. This should only happen in
|
290
290
|
# the rare cases where .related_objects is overridden anyway.
|
291
|
-
if not sub_objs.
|
291
|
+
if not sub_objs.sql_query.select_related:
|
292
292
|
referenced_fields = set(
|
293
293
|
chain.from_iterable(
|
294
294
|
(rf.attname for rf in rel.field.foreign_related_fields)
|
@@ -17,19 +17,21 @@ from plain.models.utils import resolve_callables
|
|
17
17
|
|
18
18
|
def _filter_prefetch_queryset(queryset, field_name, instances):
|
19
19
|
predicate = Q(**{f"{field_name}__in": instances})
|
20
|
-
if queryset.
|
20
|
+
if queryset.sql_query.is_sliced:
|
21
21
|
if not db_connection.features.supports_over_clause:
|
22
22
|
raise NotSupportedError(
|
23
23
|
"Prefetching from a limited queryset is only supported on backends "
|
24
24
|
"that support window functions."
|
25
25
|
)
|
26
|
-
low_mark, high_mark = queryset.
|
27
|
-
order_by = [
|
26
|
+
low_mark, high_mark = queryset.sql_query.low_mark, queryset.sql_query.high_mark
|
27
|
+
order_by = [
|
28
|
+
expr for expr, _ in queryset.sql_query.get_compiler().get_order_by()
|
29
|
+
]
|
28
30
|
window = Window(RowNumber(), partition_by=field_name, order_by=order_by)
|
29
31
|
predicate &= GreaterThan(window, low_mark)
|
30
32
|
if high_mark is not None:
|
31
33
|
predicate &= LessThanOrEqual(window, high_mark)
|
32
|
-
queryset.
|
34
|
+
queryset.sql_query.clear_limits()
|
33
35
|
return queryset.filter(predicate)
|
34
36
|
|
35
37
|
|
plain/models/query.py
CHANGED
@@ -60,7 +60,7 @@ class ModelIterable(BaseIterable):
|
|
60
60
|
|
61
61
|
def __iter__(self):
|
62
62
|
queryset = self.queryset
|
63
|
-
compiler = queryset.
|
63
|
+
compiler = queryset.sql_query.get_compiler()
|
64
64
|
# Execute the query. This will also fill compiler.select, klass_info,
|
65
65
|
# and annotations.
|
66
66
|
results = compiler.execute_sql(
|
@@ -117,7 +117,7 @@ class RawModelIterable(BaseIterable):
|
|
117
117
|
|
118
118
|
def __iter__(self):
|
119
119
|
# Cache some things for performance reasons outside the loop.
|
120
|
-
query = self.queryset.
|
120
|
+
query = self.queryset.sql_query
|
121
121
|
compiler = db_connection.ops.compiler("SQLCompiler")(query, db_connection)
|
122
122
|
query_iterator = iter(query)
|
123
123
|
|
@@ -159,7 +159,7 @@ class ValuesIterable(BaseIterable):
|
|
159
159
|
|
160
160
|
def __iter__(self):
|
161
161
|
queryset = self.queryset
|
162
|
-
query = queryset.
|
162
|
+
query = queryset.sql_query
|
163
163
|
compiler = query.get_compiler()
|
164
164
|
|
165
165
|
# extra(select=...) cols are always at the start of the row.
|
@@ -183,7 +183,7 @@ class ValuesListIterable(BaseIterable):
|
|
183
183
|
|
184
184
|
def __iter__(self):
|
185
185
|
queryset = self.queryset
|
186
|
-
query = queryset.
|
186
|
+
query = queryset.sql_query
|
187
187
|
compiler = query.get_compiler()
|
188
188
|
|
189
189
|
if queryset._fields:
|
@@ -225,7 +225,7 @@ class NamedValuesListIterable(ValuesListIterable):
|
|
225
225
|
if queryset._fields:
|
226
226
|
names = queryset._fields
|
227
227
|
else:
|
228
|
-
query = queryset.
|
228
|
+
query = queryset.sql_query
|
229
229
|
names = [
|
230
230
|
*query.extra_select,
|
231
231
|
*query.values_select,
|
@@ -245,7 +245,7 @@ class FlatValuesListIterable(BaseIterable):
|
|
245
245
|
|
246
246
|
def __iter__(self):
|
247
247
|
queryset = self.queryset
|
248
|
-
compiler = queryset.
|
248
|
+
compiler = queryset.sql_query.get_compiler()
|
249
249
|
for row in compiler.results_iter(
|
250
250
|
chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size
|
251
251
|
):
|
@@ -270,15 +270,15 @@ class QuerySet:
|
|
270
270
|
self._deferred_filter = None
|
271
271
|
|
272
272
|
@property
|
273
|
-
def
|
273
|
+
def sql_query(self):
|
274
274
|
if self._deferred_filter:
|
275
275
|
negate, args, kwargs = self._deferred_filter
|
276
276
|
self._filter_or_exclude_inplace(negate, args, kwargs)
|
277
277
|
self._deferred_filter = None
|
278
278
|
return self._query
|
279
279
|
|
280
|
-
@
|
281
|
-
def
|
280
|
+
@sql_query.setter
|
281
|
+
def sql_query(self, value):
|
282
282
|
if value.values_select:
|
283
283
|
self._iterable_class = ValuesIterable
|
284
284
|
self._query = value
|
@@ -380,11 +380,11 @@ class QuerySet:
|
|
380
380
|
stop = int(k.stop)
|
381
381
|
else:
|
382
382
|
stop = None
|
383
|
-
qs.
|
383
|
+
qs.sql_query.set_limits(start, stop)
|
384
384
|
return list(qs)[:: k.step] if k.step else qs
|
385
385
|
|
386
386
|
qs = self._chain()
|
387
|
-
qs.
|
387
|
+
qs.sql_query.set_limits(k, k + 1)
|
388
388
|
qs._fetch_all()
|
389
389
|
return qs._result_cache[0]
|
390
390
|
|
@@ -400,7 +400,7 @@ class QuerySet:
|
|
400
400
|
return self
|
401
401
|
combined = self._chain()
|
402
402
|
combined._merge_known_related_objects(other)
|
403
|
-
combined.
|
403
|
+
combined.sql_query.combine(other.sql_query, sql.AND)
|
404
404
|
return combined
|
405
405
|
|
406
406
|
def __or__(self, other):
|
@@ -412,14 +412,14 @@ class QuerySet:
|
|
412
412
|
return self
|
413
413
|
query = (
|
414
414
|
self
|
415
|
-
if self.
|
415
|
+
if self.sql_query.can_filter()
|
416
416
|
else self.model._meta.base_queryset.filter(id__in=self.values("id"))
|
417
417
|
)
|
418
418
|
combined = query._chain()
|
419
419
|
combined._merge_known_related_objects(other)
|
420
|
-
if not other.
|
420
|
+
if not other.sql_query.can_filter():
|
421
421
|
other = other.model._meta.base_queryset.filter(id__in=other.values("id"))
|
422
|
-
combined.
|
422
|
+
combined.sql_query.combine(other.sql_query, sql.OR)
|
423
423
|
return combined
|
424
424
|
|
425
425
|
def __xor__(self, other):
|
@@ -431,14 +431,14 @@ class QuerySet:
|
|
431
431
|
return self
|
432
432
|
query = (
|
433
433
|
self
|
434
|
-
if self.
|
434
|
+
if self.sql_query.can_filter()
|
435
435
|
else self.model._meta.base_queryset.filter(id__in=self.values("id"))
|
436
436
|
)
|
437
437
|
combined = query._chain()
|
438
438
|
combined._merge_known_related_objects(other)
|
439
|
-
if not other.
|
439
|
+
if not other.sql_query.can_filter():
|
440
440
|
other = other.model._meta.base_queryset.filter(id__in=other.values("id"))
|
441
|
-
combined.
|
441
|
+
combined.sql_query.combine(other.sql_query, sql.XOR)
|
442
442
|
return combined
|
443
443
|
|
444
444
|
####################################
|
@@ -487,7 +487,7 @@ class QuerySet:
|
|
487
487
|
If args is present the expression is passed as a kwarg using
|
488
488
|
the Aggregate object's default alias.
|
489
489
|
"""
|
490
|
-
if self.
|
490
|
+
if self.sql_query.distinct_fields:
|
491
491
|
raise NotImplementedError("aggregate() + distinct(fields) not implemented.")
|
492
492
|
self._validate_values_are_expressions(
|
493
493
|
(*args, *kwargs.values()), method_name="aggregate"
|
@@ -502,7 +502,7 @@ class QuerySet:
|
|
502
502
|
raise TypeError("Complex aggregates require an alias")
|
503
503
|
kwargs[arg.default_alias] = arg
|
504
504
|
|
505
|
-
return self.
|
505
|
+
return self.sql_query.chain().get_aggregation(kwargs)
|
506
506
|
|
507
507
|
def count(self):
|
508
508
|
"""
|
@@ -515,28 +515,30 @@ class QuerySet:
|
|
515
515
|
if self._result_cache is not None:
|
516
516
|
return len(self._result_cache)
|
517
517
|
|
518
|
-
return self.
|
518
|
+
return self.sql_query.get_count()
|
519
519
|
|
520
520
|
def get(self, *args, **kwargs):
|
521
521
|
"""
|
522
522
|
Perform the query and return a single object matching the given
|
523
523
|
keyword arguments.
|
524
524
|
"""
|
525
|
-
if self.
|
525
|
+
if self.sql_query.combinator and (args or kwargs):
|
526
526
|
raise NotSupportedError(
|
527
|
-
f"Calling QuerySet.get(...) with filters after {self.
|
527
|
+
f"Calling QuerySet.get(...) with filters after {self.sql_query.combinator}() is not "
|
528
528
|
"supported."
|
529
529
|
)
|
530
|
-
clone =
|
531
|
-
|
530
|
+
clone = (
|
531
|
+
self._chain() if self.sql_query.combinator else self.filter(*args, **kwargs)
|
532
|
+
)
|
533
|
+
if self.sql_query.can_filter() and not self.sql_query.distinct_fields:
|
532
534
|
clone = clone.order_by()
|
533
535
|
limit = None
|
534
536
|
if (
|
535
|
-
not clone.
|
537
|
+
not clone.sql_query.select_for_update
|
536
538
|
or db_connection.features.supports_select_for_update_with_limit
|
537
539
|
):
|
538
540
|
limit = MAX_GET_RESULTS
|
539
|
-
clone.
|
541
|
+
clone.sql_query.set_limits(high=limit)
|
540
542
|
num = len(clone)
|
541
543
|
if num == 1:
|
542
544
|
return clone._result_cache[0]
|
@@ -877,7 +879,7 @@ class QuerySet:
|
|
877
879
|
Return a dictionary mapping each of the given IDs to the object with
|
878
880
|
that ID. If `id_list` isn't provided, evaluate the entire QuerySet.
|
879
881
|
"""
|
880
|
-
if self.
|
882
|
+
if self.sql_query.is_sliced:
|
881
883
|
raise TypeError("Cannot use 'limit' or 'offset' with in_bulk().")
|
882
884
|
opts = self.model._meta
|
883
885
|
unique_fields = [
|
@@ -889,7 +891,7 @@ class QuerySet:
|
|
889
891
|
field_name != "id"
|
890
892
|
and not opts.get_field(field_name).primary_key
|
891
893
|
and field_name not in unique_fields
|
892
|
-
and self.
|
894
|
+
and self.sql_query.distinct_fields != (field_name,)
|
893
895
|
):
|
894
896
|
raise ValueError(
|
895
897
|
f"in_bulk()'s field_name must be a unique field but {field_name!r} isn't."
|
@@ -916,9 +918,9 @@ class QuerySet:
|
|
916
918
|
def delete(self):
|
917
919
|
"""Delete the records in the current QuerySet."""
|
918
920
|
self._not_support_combined_queries("delete")
|
919
|
-
if self.
|
921
|
+
if self.sql_query.is_sliced:
|
920
922
|
raise TypeError("Cannot use 'limit' or 'offset' with delete().")
|
921
|
-
if self.
|
923
|
+
if self.sql_query.distinct or self.sql_query.distinct_fields:
|
922
924
|
raise TypeError("Cannot call delete() after .distinct().")
|
923
925
|
if self._fields is not None:
|
924
926
|
raise TypeError("Cannot call delete() after .values() or .values_list()")
|
@@ -931,9 +933,9 @@ class QuerySet:
|
|
931
933
|
del_query._for_write = True
|
932
934
|
|
933
935
|
# Disable non-supported fields.
|
934
|
-
del_query.
|
935
|
-
del_query.
|
936
|
-
del_query.
|
936
|
+
del_query.sql_query.select_for_update = False
|
937
|
+
del_query.sql_query.select_related = False
|
938
|
+
del_query.sql_query.clear_ordering(force=True)
|
937
939
|
|
938
940
|
from plain.models.deletion import Collector
|
939
941
|
|
@@ -950,7 +952,7 @@ class QuerySet:
|
|
950
952
|
Delete objects found from the given queryset in single direct SQL
|
951
953
|
query. No signals are sent and there is no protection for cascades.
|
952
954
|
"""
|
953
|
-
query = self.
|
955
|
+
query = self.sql_query.clone()
|
954
956
|
query.__class__ = sql.DeleteQuery
|
955
957
|
cursor = query.get_compiler().execute_sql(CURSOR)
|
956
958
|
if cursor:
|
@@ -964,10 +966,10 @@ class QuerySet:
|
|
964
966
|
fields to the appropriate values.
|
965
967
|
"""
|
966
968
|
self._not_support_combined_queries("update")
|
967
|
-
if self.
|
969
|
+
if self.sql_query.is_sliced:
|
968
970
|
raise TypeError("Cannot update a query once a slice has been taken.")
|
969
971
|
self._for_write = True
|
970
|
-
query = self.
|
972
|
+
query = self.sql_query.chain(sql.UpdateQuery)
|
971
973
|
query.add_update_values(kwargs)
|
972
974
|
|
973
975
|
# Inline annotations in order_by(), if possible.
|
@@ -1004,9 +1006,9 @@ class QuerySet:
|
|
1004
1006
|
code (it requires too much poking around at model internals to be
|
1005
1007
|
useful at that level).
|
1006
1008
|
"""
|
1007
|
-
if self.
|
1009
|
+
if self.sql_query.is_sliced:
|
1008
1010
|
raise TypeError("Cannot update a query once a slice has been taken.")
|
1009
|
-
query = self.
|
1011
|
+
query = self.sql_query.chain(sql.UpdateQuery)
|
1010
1012
|
query.add_update_fields(values)
|
1011
1013
|
# Clear any annotations so that they won't be present in subqueries.
|
1012
1014
|
query.annotations = {}
|
@@ -1018,7 +1020,7 @@ class QuerySet:
|
|
1018
1020
|
Return True if the QuerySet would have any results, False otherwise.
|
1019
1021
|
"""
|
1020
1022
|
if self._result_cache is None:
|
1021
|
-
return self.
|
1023
|
+
return self.sql_query.has_results()
|
1022
1024
|
return bool(self._result_cache)
|
1023
1025
|
|
1024
1026
|
def contains(self, obj):
|
@@ -1052,7 +1054,7 @@ class QuerySet:
|
|
1052
1054
|
Runs an EXPLAIN on the SQL query this QuerySet would perform, and
|
1053
1055
|
returns the results.
|
1054
1056
|
"""
|
1055
|
-
return self.
|
1057
|
+
return self.sql_query.explain(format=format, **options)
|
1056
1058
|
|
1057
1059
|
##################################################
|
1058
1060
|
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
|
@@ -1073,7 +1075,7 @@ class QuerySet:
|
|
1073
1075
|
if expressions:
|
1074
1076
|
clone = clone.annotate(**expressions)
|
1075
1077
|
clone._fields = fields
|
1076
|
-
clone.
|
1078
|
+
clone.sql_query.set_values(fields)
|
1077
1079
|
return clone
|
1078
1080
|
|
1079
1081
|
def values(self, *fields, **expressions):
|
@@ -1175,7 +1177,7 @@ class QuerySet:
|
|
1175
1177
|
def none(self):
|
1176
1178
|
"""Return an empty QuerySet."""
|
1177
1179
|
clone = self._chain()
|
1178
|
-
clone.
|
1180
|
+
clone.sql_query.set_empty()
|
1179
1181
|
return clone
|
1180
1182
|
|
1181
1183
|
##################################################################
|
@@ -1206,7 +1208,7 @@ class QuerySet:
|
|
1206
1208
|
return self._filter_or_exclude(True, args, kwargs)
|
1207
1209
|
|
1208
1210
|
def _filter_or_exclude(self, negate, args, kwargs):
|
1209
|
-
if (args or kwargs) and self.
|
1211
|
+
if (args or kwargs) and self.sql_query.is_sliced:
|
1210
1212
|
raise TypeError("Cannot filter a query once a slice has been taken.")
|
1211
1213
|
clone = self._chain()
|
1212
1214
|
if self._defer_next_filter:
|
@@ -1234,7 +1236,7 @@ class QuerySet:
|
|
1234
1236
|
"""
|
1235
1237
|
if isinstance(filter_obj, Q):
|
1236
1238
|
clone = self._chain()
|
1237
|
-
clone.
|
1239
|
+
clone.sql_query.add_q(filter_obj)
|
1238
1240
|
return clone
|
1239
1241
|
else:
|
1240
1242
|
return self._filter_or_exclude(False, args=(), kwargs=filter_obj)
|
@@ -1243,13 +1245,13 @@ class QuerySet:
|
|
1243
1245
|
# Clone the query to inherit the select list and everything
|
1244
1246
|
clone = self._chain()
|
1245
1247
|
# Clear limits and ordering so they can be reapplied
|
1246
|
-
clone.
|
1247
|
-
clone.
|
1248
|
-
clone.
|
1249
|
-
qs.
|
1248
|
+
clone.sql_query.clear_ordering(force=True)
|
1249
|
+
clone.sql_query.clear_limits()
|
1250
|
+
clone.sql_query.combined_queries = (self.sql_query,) + tuple(
|
1251
|
+
qs.sql_query for qs in other_qs
|
1250
1252
|
)
|
1251
|
-
clone.
|
1252
|
-
clone.
|
1253
|
+
clone.sql_query.combinator = combinator
|
1254
|
+
clone.sql_query.combinator_all = all
|
1253
1255
|
return clone
|
1254
1256
|
|
1255
1257
|
def union(self, *other_qs, all=False):
|
@@ -1287,11 +1289,11 @@ class QuerySet:
|
|
1287
1289
|
raise ValueError("The nowait option cannot be used with skip_locked.")
|
1288
1290
|
obj = self._chain()
|
1289
1291
|
obj._for_write = True
|
1290
|
-
obj.
|
1291
|
-
obj.
|
1292
|
-
obj.
|
1293
|
-
obj.
|
1294
|
-
obj.
|
1292
|
+
obj.sql_query.select_for_update = True
|
1293
|
+
obj.sql_query.select_for_update_nowait = nowait
|
1294
|
+
obj.sql_query.select_for_update_skip_locked = skip_locked
|
1295
|
+
obj.sql_query.select_for_update_of = of
|
1296
|
+
obj.sql_query.select_for_no_key_update = no_key
|
1295
1297
|
return obj
|
1296
1298
|
|
1297
1299
|
def select_related(self, *fields):
|
@@ -1311,11 +1313,11 @@ class QuerySet:
|
|
1311
1313
|
|
1312
1314
|
obj = self._chain()
|
1313
1315
|
if fields == (None,):
|
1314
|
-
obj.
|
1316
|
+
obj.sql_query.select_related = False
|
1315
1317
|
elif fields:
|
1316
|
-
obj.
|
1318
|
+
obj.sql_query.add_select_related(fields)
|
1317
1319
|
else:
|
1318
|
-
obj.
|
1320
|
+
obj.sql_query.select_related = True
|
1319
1321
|
return obj
|
1320
1322
|
|
1321
1323
|
def prefetch_related(self, *lookups):
|
@@ -1336,7 +1338,7 @@ class QuerySet:
|
|
1336
1338
|
if isinstance(lookup, Prefetch):
|
1337
1339
|
lookup = lookup.prefetch_to
|
1338
1340
|
lookup = lookup.split(LOOKUP_SEP, 1)[0]
|
1339
|
-
if lookup in self.
|
1341
|
+
if lookup in self.sql_query._filtered_relations:
|
1340
1342
|
raise ValueError(
|
1341
1343
|
"prefetch_related() is not supported with FilteredRelation."
|
1342
1344
|
)
|
@@ -1394,30 +1396,30 @@ class QuerySet:
|
|
1394
1396
|
f"The annotation '{alias}' conflicts with a field on the model."
|
1395
1397
|
)
|
1396
1398
|
if isinstance(annotation, FilteredRelation):
|
1397
|
-
clone.
|
1399
|
+
clone.sql_query.add_filtered_relation(annotation, alias)
|
1398
1400
|
else:
|
1399
|
-
clone.
|
1401
|
+
clone.sql_query.add_annotation(
|
1400
1402
|
annotation,
|
1401
1403
|
alias,
|
1402
1404
|
select=select,
|
1403
1405
|
)
|
1404
|
-
for alias, annotation in clone.
|
1406
|
+
for alias, annotation in clone.sql_query.annotations.items():
|
1405
1407
|
if alias in annotations and annotation.contains_aggregate:
|
1406
1408
|
if clone._fields is None:
|
1407
|
-
clone.
|
1409
|
+
clone.sql_query.group_by = True
|
1408
1410
|
else:
|
1409
|
-
clone.
|
1411
|
+
clone.sql_query.set_group_by()
|
1410
1412
|
break
|
1411
1413
|
|
1412
1414
|
return clone
|
1413
1415
|
|
1414
1416
|
def order_by(self, *field_names):
|
1415
1417
|
"""Return a new QuerySet instance with the ordering changed."""
|
1416
|
-
if self.
|
1418
|
+
if self.sql_query.is_sliced:
|
1417
1419
|
raise TypeError("Cannot reorder a query once a slice has been taken.")
|
1418
1420
|
obj = self._chain()
|
1419
|
-
obj.
|
1420
|
-
obj.
|
1421
|
+
obj.sql_query.clear_ordering(force=True, clear_default=False)
|
1422
|
+
obj.sql_query.add_ordering(*field_names)
|
1421
1423
|
return obj
|
1422
1424
|
|
1423
1425
|
def distinct(self, *field_names):
|
@@ -1425,12 +1427,12 @@ class QuerySet:
|
|
1425
1427
|
Return a new QuerySet instance that will select only distinct results.
|
1426
1428
|
"""
|
1427
1429
|
self._not_support_combined_queries("distinct")
|
1428
|
-
if self.
|
1430
|
+
if self.sql_query.is_sliced:
|
1429
1431
|
raise TypeError(
|
1430
1432
|
"Cannot create distinct fields once a slice has been taken."
|
1431
1433
|
)
|
1432
1434
|
obj = self._chain()
|
1433
|
-
obj.
|
1435
|
+
obj.sql_query.add_distinct_fields(*field_names)
|
1434
1436
|
return obj
|
1435
1437
|
|
1436
1438
|
def extra(
|
@@ -1444,18 +1446,20 @@ class QuerySet:
|
|
1444
1446
|
):
|
1445
1447
|
"""Add extra SQL fragments to the query."""
|
1446
1448
|
self._not_support_combined_queries("extra")
|
1447
|
-
if self.
|
1449
|
+
if self.sql_query.is_sliced:
|
1448
1450
|
raise TypeError("Cannot change a query once a slice has been taken.")
|
1449
1451
|
clone = self._chain()
|
1450
|
-
clone.
|
1452
|
+
clone.sql_query.add_extra(
|
1453
|
+
select, select_params, where, params, tables, order_by
|
1454
|
+
)
|
1451
1455
|
return clone
|
1452
1456
|
|
1453
1457
|
def reverse(self):
|
1454
1458
|
"""Reverse the ordering of the QuerySet."""
|
1455
|
-
if self.
|
1459
|
+
if self.sql_query.is_sliced:
|
1456
1460
|
raise TypeError("Cannot reverse a query once a slice has been taken.")
|
1457
1461
|
clone = self._chain()
|
1458
|
-
clone.
|
1462
|
+
clone.sql_query.standard_ordering = not clone.sql_query.standard_ordering
|
1459
1463
|
return clone
|
1460
1464
|
|
1461
1465
|
def defer(self, *fields):
|
@@ -1470,9 +1474,9 @@ class QuerySet:
|
|
1470
1474
|
raise TypeError("Cannot call defer() after .values() or .values_list()")
|
1471
1475
|
clone = self._chain()
|
1472
1476
|
if fields == (None,):
|
1473
|
-
clone.
|
1477
|
+
clone.sql_query.clear_deferred_loading()
|
1474
1478
|
else:
|
1475
|
-
clone.
|
1479
|
+
clone.sql_query.add_deferred_loading(fields)
|
1476
1480
|
return clone
|
1477
1481
|
|
1478
1482
|
def only(self, *fields):
|
@@ -1490,10 +1494,10 @@ class QuerySet:
|
|
1490
1494
|
raise TypeError("Cannot pass None as an argument to only().")
|
1491
1495
|
for field in fields:
|
1492
1496
|
field = field.split(LOOKUP_SEP, 1)[0]
|
1493
|
-
if field in self.
|
1497
|
+
if field in self.sql_query._filtered_relations:
|
1494
1498
|
raise ValueError("only() is not supported with FilteredRelation.")
|
1495
1499
|
clone = self._chain()
|
1496
|
-
clone.
|
1500
|
+
clone.sql_query.add_immediate_loading(fields)
|
1497
1501
|
return clone
|
1498
1502
|
|
1499
1503
|
###################################
|
@@ -1508,14 +1512,14 @@ class QuerySet:
|
|
1508
1512
|
"""
|
1509
1513
|
if isinstance(self, EmptyQuerySet):
|
1510
1514
|
return True
|
1511
|
-
if self.
|
1515
|
+
if self.sql_query.extra_order_by or self.sql_query.order_by:
|
1512
1516
|
return True
|
1513
1517
|
elif (
|
1514
|
-
self.
|
1515
|
-
and self.
|
1518
|
+
self.sql_query.default_ordering
|
1519
|
+
and self.sql_query.get_meta().ordering
|
1516
1520
|
and
|
1517
1521
|
# A default ordering doesn't affect GROUP BY queries.
|
1518
|
-
not self.
|
1522
|
+
not self.sql_query.group_by
|
1519
1523
|
):
|
1520
1524
|
return True
|
1521
1525
|
else:
|
@@ -1592,7 +1596,7 @@ class QuerySet:
|
|
1592
1596
|
"""
|
1593
1597
|
obj = self._clone()
|
1594
1598
|
if obj._sticky_filter:
|
1595
|
-
obj.
|
1599
|
+
obj.sql_query.filter_is_sticky = True
|
1596
1600
|
obj._sticky_filter = False
|
1597
1601
|
return obj
|
1598
1602
|
|
@@ -1603,7 +1607,7 @@ class QuerySet:
|
|
1603
1607
|
"""
|
1604
1608
|
c = self.__class__(
|
1605
1609
|
model=self.model,
|
1606
|
-
query=self.
|
1610
|
+
query=self.sql_query.chain(),
|
1607
1611
|
)
|
1608
1612
|
c._sticky_filter = self._sticky_filter
|
1609
1613
|
c._for_write = self._for_write
|
@@ -1636,9 +1640,10 @@ class QuerySet:
|
|
1636
1640
|
def _merge_sanity_check(self, other):
|
1637
1641
|
"""Check that two QuerySet classes may be merged."""
|
1638
1642
|
if self._fields is not None and (
|
1639
|
-
set(self.
|
1640
|
-
or set(self.
|
1641
|
-
or set(self.
|
1643
|
+
set(self.sql_query.values_select) != set(other.sql_query.values_select)
|
1644
|
+
or set(self.sql_query.extra_select) != set(other.sql_query.extra_select)
|
1645
|
+
or set(self.sql_query.annotation_select)
|
1646
|
+
!= set(other.sql_query.annotation_select)
|
1642
1647
|
):
|
1643
1648
|
raise TypeError(
|
1644
1649
|
f"Merging '{self.__class__.__name__}' classes must involve the same values in each case."
|
@@ -1656,7 +1661,7 @@ class QuerySet:
|
|
1656
1661
|
# values() queryset can only be used as nested queries
|
1657
1662
|
# if they are set up to select only a single field.
|
1658
1663
|
raise TypeError("Cannot use multi-field values as a filter value.")
|
1659
|
-
query = self.
|
1664
|
+
query = self.sql_query.resolve_expression(*args, **kwargs)
|
1660
1665
|
return query
|
1661
1666
|
|
1662
1667
|
def _has_filters(self):
|
@@ -1665,7 +1670,7 @@ class QuerySet:
|
|
1665
1670
|
equivalent with checking if all objects are present in results, for
|
1666
1671
|
example, qs[1:]._has_filters() -> False.
|
1667
1672
|
"""
|
1668
|
-
return self.
|
1673
|
+
return self.sql_query.has_filters()
|
1669
1674
|
|
1670
1675
|
@staticmethod
|
1671
1676
|
def _validate_values_are_expressions(values, method_name):
|
@@ -1681,19 +1686,19 @@ class QuerySet:
|
|
1681
1686
|
)
|
1682
1687
|
|
1683
1688
|
def _not_support_combined_queries(self, operation_name):
|
1684
|
-
if self.
|
1689
|
+
if self.sql_query.combinator:
|
1685
1690
|
raise NotSupportedError(
|
1686
|
-
f"Calling QuerySet.{operation_name}() after {self.
|
1691
|
+
f"Calling QuerySet.{operation_name}() after {self.sql_query.combinator}() is not supported."
|
1687
1692
|
)
|
1688
1693
|
|
1689
1694
|
def _check_operator_queryset(self, other, operator_):
|
1690
|
-
if self.
|
1695
|
+
if self.sql_query.combinator or other.sql_query.combinator:
|
1691
1696
|
raise TypeError(f"Cannot use {operator_} operator with combined queryset.")
|
1692
1697
|
|
1693
1698
|
|
1694
1699
|
class InstanceCheckMeta(type):
|
1695
1700
|
def __instancecheck__(self, instance):
|
1696
|
-
return isinstance(instance, QuerySet) and instance.
|
1701
|
+
return isinstance(instance, QuerySet) and instance.sql_query.is_empty()
|
1697
1702
|
|
1698
1703
|
|
1699
1704
|
class EmptyQuerySet(metaclass=InstanceCheckMeta):
|
@@ -1722,7 +1727,7 @@ class RawQuerySet:
|
|
1722
1727
|
):
|
1723
1728
|
self.raw_query = raw_query
|
1724
1729
|
self.model = model
|
1725
|
-
self.
|
1730
|
+
self.sql_query = query or sql.RawQuery(sql=raw_query, params=params)
|
1726
1731
|
self.params = params
|
1727
1732
|
self.translations = translations or {}
|
1728
1733
|
self._result_cache = None
|
@@ -1764,7 +1769,7 @@ class RawQuerySet:
|
|
1764
1769
|
c = self.__class__(
|
1765
1770
|
self.raw_query,
|
1766
1771
|
model=self.model,
|
1767
|
-
query=self.
|
1772
|
+
query=self.sql_query,
|
1768
1773
|
params=self.params,
|
1769
1774
|
translations=self.translations,
|
1770
1775
|
)
|
@@ -1793,7 +1798,7 @@ class RawQuerySet:
|
|
1793
1798
|
yield from RawModelIterable(self)
|
1794
1799
|
|
1795
1800
|
def __repr__(self):
|
1796
|
-
return f"<{self.__class__.__name__}: {self.
|
1801
|
+
return f"<{self.__class__.__name__}: {self.sql_query}>"
|
1797
1802
|
|
1798
1803
|
def __getitem__(self, k):
|
1799
1804
|
return list(self)[k]
|
@@ -1804,7 +1809,7 @@ class RawQuerySet:
|
|
1804
1809
|
A list of model field names in the order they'll appear in the
|
1805
1810
|
query results.
|
1806
1811
|
"""
|
1807
|
-
columns = self.
|
1812
|
+
columns = self.sql_query.get_columns()
|
1808
1813
|
# Adjust any column names which don't match field names
|
1809
1814
|
for query_name, model_name in self.translations.items():
|
1810
1815
|
# Ignore translations for nonexistent column names
|
@@ -1,5 +1,5 @@
|
|
1
1
|
plain/models/AGENTS.md,sha256=xQQW-z-DehnCUyjiGSBfLqUjoSUdo_W1b0JmwYmWieA,209
|
2
|
-
plain/models/CHANGELOG.md,sha256=
|
2
|
+
plain/models/CHANGELOG.md,sha256=JMkQ7LwLIp63_HtRIwOMdIpveUrhHvIBJbvBDaNG9k4,17633
|
3
3
|
plain/models/README.md,sha256=lqzWJrEIxBCHC1P8X1YoRjbsMFlu0-kG4ujP76B_ZO4,8572
|
4
4
|
plain/models/__init__.py,sha256=aB9HhIKBh0iK3LZztInAE-rDF-yKsdfcjfMtwtN5vnI,2920
|
5
5
|
plain/models/aggregates.py,sha256=P0mhsMl1VZt2CVHMuCHnNI8SxZ9citjDLEgioN6NOpo,7240
|
@@ -12,7 +12,7 @@ plain/models/constraints.py,sha256=Mm9gm5D7EKmo486dL481-hrTcxi2gxgqyUUtbGrkLjs,1
|
|
12
12
|
plain/models/database_url.py,sha256=iidKVhOylf5N6t1EMPRySRQiv6LiuRjYRECB_UJ3MI8,6419
|
13
13
|
plain/models/db.py,sha256=FpdfLYrRX2THUzDy4QdJ_OpSo9IFKLerZIEQ-T2x8zA,1348
|
14
14
|
plain/models/default_settings.py,sha256=cDym1o_DtHySWgDRIdjgEM0YxjgYU51ZqzWVA3vpzTk,569
|
15
|
-
plain/models/deletion.py,sha256=
|
15
|
+
plain/models/deletion.py,sha256=GnjhPHLRAsrec7Aa0sczqtOEDV41AGKj6QBQdhl508A,17611
|
16
16
|
plain/models/entrypoints.py,sha256=EC14mW19tK9dCumaNHnv4_9jQV8jomQ8jXy8Ib89VBw,191
|
17
17
|
plain/models/enums.py,sha256=Zr-JKt2aeYsSADtAm69fDRfajS7jYwop2vWQVLJ9YYI,2726
|
18
18
|
plain/models/exceptions.py,sha256=IqzK60-hY3TYsgOMxlWwgpVa21E7ydC-gqUG4tNvVJc,2042
|
@@ -23,7 +23,7 @@ plain/models/lookups.py,sha256=eCsxQXUcOoAa_U_fAAd3edcgXI1wfyFW8hPgUh8TwTo,24776
|
|
23
23
|
plain/models/options.py,sha256=BOnu9NDVcgL0tJhan5gBbaK1SWNeg4NVTPNAzkKT3NE,21528
|
24
24
|
plain/models/otel.py,sha256=36QSJS6UXv1YPJTqeSmEvdMVHRkXa_zgqqItJaXc59g,7619
|
25
25
|
plain/models/preflight.py,sha256=_cBX7AnfQDNtZfoW0ydxH8WQM3ftCqcH0-tPhqS5q8c,8973
|
26
|
-
plain/models/query.py,sha256=
|
26
|
+
plain/models/query.py,sha256=YxIpgRndQyXsSpNLl3uPGvu-UNQzK1CXFsf2U75XVao,90714
|
27
27
|
plain/models/query_utils.py,sha256=zxAdfwDbOmaN_SJODl4Wl9gs-q2EzOjXbsBFTWWhh8g,14174
|
28
28
|
plain/models/registry.py,sha256=5yxVgT_W8GlyL2bsGT2HvMQB5sKolXucP2qrhr7Wlnk,8126
|
29
29
|
plain/models/transaction.py,sha256=KqkRDT6aqMgbPA_ch7qO8a9NyDvwY_2FaxM7FkBkcgY,9357
|
@@ -77,7 +77,7 @@ plain/models/fields/mixins.py,sha256=wmu3JJEOimijgepjMEFPN8u74mHpefgnsB4u5ZzVCUY
|
|
77
77
|
plain/models/fields/related.py,sha256=7Ku8E5hxQxVgJy3afWZfuDlHDzrky_4mkKwPlqLpM6o,51065
|
78
78
|
plain/models/fields/related_descriptors.py,sha256=nsVgLjpOlrha90eTfg7ad_il6_uI_YG0d4bH51LP3Os,15180
|
79
79
|
plain/models/fields/related_lookups.py,sha256=9y6AfEcg8xRRZne2LXFP6jym9mecFlB_toYih7lD8Uw,7781
|
80
|
-
plain/models/fields/related_managers.py,sha256=
|
80
|
+
plain/models/fields/related_managers.py,sha256=LefSf8ss8XZ_97NoKfRKHTijheb44exJ_UBLGcNgQdc,25053
|
81
81
|
plain/models/fields/reverse_related.py,sha256=SNFytCI3BeAlB5kY6UQrv6QGqtRMo_aHWPx_-fCkfu4,10404
|
82
82
|
plain/models/functions/__init__.py,sha256=aglCm_JtzDYk2KmxubDN_78CGG3JCfRWnfJ74Oj5YJ4,2658
|
83
83
|
plain/models/functions/comparison.py,sha256=9uAiEuNXZiGFzJKBvktsHwx58Qpa2cPQkr6pUWsGcKo,6554
|
@@ -115,8 +115,8 @@ plain/models/sql/where.py,sha256=ezE9Clt2BmKo-I7ARsgqZ_aVA-1UdayCwr6ULSWZL6c,126
|
|
115
115
|
plain/models/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
116
116
|
plain/models/test/pytest.py,sha256=KD5-mxonBxOYIhUh9Ql5uJOIiC9R4t-LYfb6sjA0UdE,3486
|
117
117
|
plain/models/test/utils.py,sha256=S3d6zf3OFWDxB_kBJr0tDvwn51bjwDVWKPumv37N-p8,467
|
118
|
-
plain_models-0.
|
119
|
-
plain_models-0.
|
120
|
-
plain_models-0.
|
121
|
-
plain_models-0.
|
122
|
-
plain_models-0.
|
118
|
+
plain_models-0.47.0.dist-info/METADATA,sha256=oBEWDFezcMc1KxtVoQIMSEr15Oy7NB43aLiAGurmYfg,8884
|
119
|
+
plain_models-0.47.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
120
|
+
plain_models-0.47.0.dist-info/entry_points.txt,sha256=IYJAW9MpL3PXyXFWmKmALagAGXC_5rzBn2eEGJlcV04,112
|
121
|
+
plain_models-0.47.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
|
122
|
+
plain_models-0.47.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|