plain.models
Model your data and store it in a database.
# app/users/models.py
from plain import models
from plain.passwords.models import PasswordField
class User(models.Model):
email = models.EmailField()
password = PasswordField()
is_admin = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.email
Create, update, and delete instances of your models:
from .models import User
# Create a new user
user = User.objects.create(
email="[email protected]",
password="password",
)
# Update a user
user.email = "[email protected]"
user.save()
# Delete a user
user.delete()
# Query for users
admin_users = User.objects.filter(is_admin=True)
Installation
# app/settings.py
INSTALLED_PACKAGES = [
...
"plain.models",
]
To connect to a database, you can provide a DATABASE_URL
environment variable.
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
Or you can manually define the DATABASES
setting.
# app/settings.py
DATABASES = {
"default": {
"ENGINE": "plain.models.backends.postgresql",
"NAME": "dbname",
"USER": "user",
"PASSWORD": "password",
"HOST": "localhost",
"PORT": "5432",
}
}
Multiple backends are supported, including Postgres, MySQL, and SQLite.
Querying
Migrations
Fields
Validation
Indexes and constraints
Managers
Forms
1"""
2The main QuerySet implementation. This provides the public API for the ORM.
3"""
4
5import copy
6import operator
7import warnings
8from itertools import chain, islice
9
10import plain.runtime
11from plain import exceptions
12from plain.exceptions import ValidationError
13from plain.models import (
14 sql,
15 transaction,
16)
17from plain.models.constants import LOOKUP_SEP, OnConflict
18from plain.models.db import (
19 PLAIN_VERSION_PICKLE_KEY,
20 IntegrityError,
21 NotSupportedError,
22 connections,
23 router,
24)
25from plain.models.expressions import Case, F, Value, When
26from plain.models.fields import (
27 AutoField,
28 DateField,
29 DateTimeField,
30 Field,
31)
32from plain.models.functions import Cast, Trunc
33from plain.models.query_utils import FilteredRelation, Q
34from plain.models.sql.constants import CURSOR, GET_ITERATOR_CHUNK_SIZE
35from plain.models.utils import (
36 create_namedtuple_class,
37 resolve_callables,
38)
39from plain.utils import timezone
40from plain.utils.functional import cached_property, partition
41
42# The maximum number of results to fetch in a get() query.
43MAX_GET_RESULTS = 21
44
45# The maximum number of items to display in a QuerySet.__repr__
46REPR_OUTPUT_SIZE = 20
47
48
49class BaseIterable:
50 def __init__(
51 self, queryset, chunked_fetch=False, chunk_size=GET_ITERATOR_CHUNK_SIZE
52 ):
53 self.queryset = queryset
54 self.chunked_fetch = chunked_fetch
55 self.chunk_size = chunk_size
56
57
58class ModelIterable(BaseIterable):
59 """Iterable that yields a model instance for each row."""
60
61 def __iter__(self):
62 queryset = self.queryset
63 db = queryset.db
64 compiler = queryset.query.get_compiler(using=db)
65 # Execute the query. This will also fill compiler.select, klass_info,
66 # and annotations.
67 results = compiler.execute_sql(
68 chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size
69 )
70 select, klass_info, annotation_col_map = (
71 compiler.select,
72 compiler.klass_info,
73 compiler.annotation_col_map,
74 )
75 model_cls = klass_info["model"]
76 select_fields = klass_info["select_fields"]
77 model_fields_start, model_fields_end = select_fields[0], select_fields[-1] + 1
78 init_list = [
79 f[0].target.attname for f in select[model_fields_start:model_fields_end]
80 ]
81 related_populators = get_related_populators(klass_info, select, db)
82 known_related_objects = [
83 (
84 field,
85 related_objs,
86 operator.attrgetter(
87 *[
88 field.attname
89 if from_field == "self"
90 else queryset.model._meta.get_field(from_field).attname
91 for from_field in field.from_fields
92 ]
93 ),
94 )
95 for field, related_objs in queryset._known_related_objects.items()
96 ]
97 for row in compiler.results_iter(results):
98 obj = model_cls.from_db(
99 db, init_list, row[model_fields_start:model_fields_end]
100 )
101 for rel_populator in related_populators:
102 rel_populator.populate(row, obj)
103 if annotation_col_map:
104 for attr_name, col_pos in annotation_col_map.items():
105 setattr(obj, attr_name, row[col_pos])
106
107 # Add the known related objects to the model.
108 for field, rel_objs, rel_getter in known_related_objects:
109 # Avoid overwriting objects loaded by, e.g., select_related().
110 if field.is_cached(obj):
111 continue
112 rel_obj_id = rel_getter(obj)
113 try:
114 rel_obj = rel_objs[rel_obj_id]
115 except KeyError:
116 pass # May happen in qs1 | qs2 scenarios.
117 else:
118 setattr(obj, field.name, rel_obj)
119
120 yield obj
121
122
123class RawModelIterable(BaseIterable):
124 """
125 Iterable that yields a model instance for each row from a raw queryset.
126 """
127
128 def __iter__(self):
129 # Cache some things for performance reasons outside the loop.
130 db = self.queryset.db
131 query = self.queryset.query
132 connection = connections[db]
133 compiler = connection.ops.compiler("SQLCompiler")(query, connection, db)
134 query_iterator = iter(query)
135
136 try:
137 (
138 model_init_names,
139 model_init_pos,
140 annotation_fields,
141 ) = self.queryset.resolve_model_init_order()
142 model_cls = self.queryset.model
143 if model_cls._meta.pk.attname not in model_init_names:
144 raise exceptions.FieldDoesNotExist(
145 "Raw query must include the primary key"
146 )
147 fields = [self.queryset.model_fields.get(c) for c in self.queryset.columns]
148 converters = compiler.get_converters(
149 [f.get_col(f.model._meta.db_table) if f else None for f in fields]
150 )
151 if converters:
152 query_iterator = compiler.apply_converters(query_iterator, converters)
153 for values in query_iterator:
154 # Associate fields to values
155 model_init_values = [values[pos] for pos in model_init_pos]
156 instance = model_cls.from_db(db, model_init_names, model_init_values)
157 if annotation_fields:
158 for column, pos in annotation_fields:
159 setattr(instance, column, values[pos])
160 yield instance
161 finally:
162 # Done iterating the Query. If it has its own cursor, close it.
163 if hasattr(query, "cursor") and query.cursor:
164 query.cursor.close()
165
166
167class ValuesIterable(BaseIterable):
168 """
169 Iterable returned by QuerySet.values() that yields a dict for each row.
170 """
171
172 def __iter__(self):
173 queryset = self.queryset
174 query = queryset.query
175 compiler = query.get_compiler(queryset.db)
176
177 # extra(select=...) cols are always at the start of the row.
178 names = [
179 *query.extra_select,
180 *query.values_select,
181 *query.annotation_select,
182 ]
183 indexes = range(len(names))
184 for row in compiler.results_iter(
185 chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size
186 ):
187 yield {names[i]: row[i] for i in indexes}
188
189
190class ValuesListIterable(BaseIterable):
191 """
192 Iterable returned by QuerySet.values_list(flat=False) that yields a tuple
193 for each row.
194 """
195
196 def __iter__(self):
197 queryset = self.queryset
198 query = queryset.query
199 compiler = query.get_compiler(queryset.db)
200
201 if queryset._fields:
202 # extra(select=...) cols are always at the start of the row.
203 names = [
204 *query.extra_select,
205 *query.values_select,
206 *query.annotation_select,
207 ]
208 fields = [
209 *queryset._fields,
210 *(f for f in query.annotation_select if f not in queryset._fields),
211 ]
212 if fields != names:
213 # Reorder according to fields.
214 index_map = {name: idx for idx, name in enumerate(names)}
215 rowfactory = operator.itemgetter(*[index_map[f] for f in fields])
216 return map(
217 rowfactory,
218 compiler.results_iter(
219 chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size
220 ),
221 )
222 return compiler.results_iter(
223 tuple_expected=True,
224 chunked_fetch=self.chunked_fetch,
225 chunk_size=self.chunk_size,
226 )
227
228
229class NamedValuesListIterable(ValuesListIterable):
230 """
231 Iterable returned by QuerySet.values_list(named=True) that yields a
232 namedtuple for each row.
233 """
234
235 def __iter__(self):
236 queryset = self.queryset
237 if queryset._fields:
238 names = queryset._fields
239 else:
240 query = queryset.query
241 names = [
242 *query.extra_select,
243 *query.values_select,
244 *query.annotation_select,
245 ]
246 tuple_class = create_namedtuple_class(*names)
247 new = tuple.__new__
248 for row in super().__iter__():
249 yield new(tuple_class, row)
250
251
252class FlatValuesListIterable(BaseIterable):
253 """
254 Iterable returned by QuerySet.values_list(flat=True) that yields single
255 values.
256 """
257
258 def __iter__(self):
259 queryset = self.queryset
260 compiler = queryset.query.get_compiler(queryset.db)
261 for row in compiler.results_iter(
262 chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size
263 ):
264 yield row[0]
265
266
267class QuerySet:
268 """Represent a lazy database lookup for a set of objects."""
269
270 def __init__(self, model=None, query=None, using=None, hints=None):
271 self.model = model
272 self._db = using
273 self._hints = hints or {}
274 self._query = query or sql.Query(self.model)
275 self._result_cache = None
276 self._sticky_filter = False
277 self._for_write = False
278 self._prefetch_related_lookups = ()
279 self._prefetch_done = False
280 self._known_related_objects = {} # {rel_field: {pk: rel_obj}}
281 self._iterable_class = ModelIterable
282 self._fields = None
283 self._defer_next_filter = False
284 self._deferred_filter = None
285
286 @property
287 def query(self):
288 if self._deferred_filter:
289 negate, args, kwargs = self._deferred_filter
290 self._filter_or_exclude_inplace(negate, args, kwargs)
291 self._deferred_filter = None
292 return self._query
293
294 @query.setter
295 def query(self, value):
296 if value.values_select:
297 self._iterable_class = ValuesIterable
298 self._query = value
299
300 def as_manager(cls):
301 # Address the circular dependency between `Queryset` and `Manager`.
302 from plain.models.manager import Manager
303
304 manager = Manager.from_queryset(cls)()
305 manager._built_with_as_manager = True
306 return manager
307
308 as_manager.queryset_only = True
309 as_manager = classmethod(as_manager)
310
311 ########################
312 # PYTHON MAGIC METHODS #
313 ########################
314
315 def __deepcopy__(self, memo):
316 """Don't populate the QuerySet's cache."""
317 obj = self.__class__()
318 for k, v in self.__dict__.items():
319 if k == "_result_cache":
320 obj.__dict__[k] = None
321 else:
322 obj.__dict__[k] = copy.deepcopy(v, memo)
323 return obj
324
325 def __getstate__(self):
326 # Force the cache to be fully populated.
327 self._fetch_all()
328 return {**self.__dict__, PLAIN_VERSION_PICKLE_KEY: plain.runtime.__version__}
329
330 def __setstate__(self, state):
331 pickled_version = state.get(PLAIN_VERSION_PICKLE_KEY)
332 if pickled_version:
333 if pickled_version != plain.runtime.__version__:
334 warnings.warn(
335 f"Pickled queryset instance's Plain version {pickled_version} does not "
336 f"match the current version {plain.runtime.__version__}.",
337 RuntimeWarning,
338 stacklevel=2,
339 )
340 else:
341 warnings.warn(
342 "Pickled queryset instance's Plain version is not specified.",
343 RuntimeWarning,
344 stacklevel=2,
345 )
346 self.__dict__.update(state)
347
348 def __repr__(self):
349 data = list(self[: REPR_OUTPUT_SIZE + 1])
350 if len(data) > REPR_OUTPUT_SIZE:
351 data[-1] = "...(remaining elements truncated)..."
352 return f"<{self.__class__.__name__} {data!r}>"
353
354 def __len__(self):
355 self._fetch_all()
356 return len(self._result_cache)
357
358 def __iter__(self):
359 """
360 The queryset iterator protocol uses three nested iterators in the
361 default case:
362 1. sql.compiler.execute_sql()
363 - Returns 100 rows at time (constants.GET_ITERATOR_CHUNK_SIZE)
364 using cursor.fetchmany(). This part is responsible for
365 doing some column masking, and returning the rows in chunks.
366 2. sql.compiler.results_iter()
367 - Returns one row at time. At this point the rows are still just
368 tuples. In some cases the return values are converted to
369 Python values at this location.
370 3. self.iterator()
371 - Responsible for turning the rows into model objects.
372 """
373 self._fetch_all()
374 return iter(self._result_cache)
375
376 def __bool__(self):
377 self._fetch_all()
378 return bool(self._result_cache)
379
380 def __getitem__(self, k):
381 """Retrieve an item or slice from the set of results."""
382 if not isinstance(k, int | slice):
383 raise TypeError(
384 f"QuerySet indices must be integers or slices, not {type(k).__name__}."
385 )
386 if (isinstance(k, int) and k < 0) or (
387 isinstance(k, slice)
388 and (
389 (k.start is not None and k.start < 0)
390 or (k.stop is not None and k.stop < 0)
391 )
392 ):
393 raise ValueError("Negative indexing is not supported.")
394
395 if self._result_cache is not None:
396 return self._result_cache[k]
397
398 if isinstance(k, slice):
399 qs = self._chain()
400 if k.start is not None:
401 start = int(k.start)
402 else:
403 start = None
404 if k.stop is not None:
405 stop = int(k.stop)
406 else:
407 stop = None
408 qs.query.set_limits(start, stop)
409 return list(qs)[:: k.step] if k.step else qs
410
411 qs = self._chain()
412 qs.query.set_limits(k, k + 1)
413 qs._fetch_all()
414 return qs._result_cache[0]
415
416 def __class_getitem__(cls, *args, **kwargs):
417 return cls
418
419 def __and__(self, other):
420 self._check_operator_queryset(other, "&")
421 self._merge_sanity_check(other)
422 if isinstance(other, EmptyQuerySet):
423 return other
424 if isinstance(self, EmptyQuerySet):
425 return self
426 combined = self._chain()
427 combined._merge_known_related_objects(other)
428 combined.query.combine(other.query, sql.AND)
429 return combined
430
431 def __or__(self, other):
432 self._check_operator_queryset(other, "|")
433 self._merge_sanity_check(other)
434 if isinstance(self, EmptyQuerySet):
435 return other
436 if isinstance(other, EmptyQuerySet):
437 return self
438 query = (
439 self
440 if self.query.can_filter()
441 else self.model._base_manager.filter(pk__in=self.values("pk"))
442 )
443 combined = query._chain()
444 combined._merge_known_related_objects(other)
445 if not other.query.can_filter():
446 other = other.model._base_manager.filter(pk__in=other.values("pk"))
447 combined.query.combine(other.query, sql.OR)
448 return combined
449
450 def __xor__(self, other):
451 self._check_operator_queryset(other, "^")
452 self._merge_sanity_check(other)
453 if isinstance(self, EmptyQuerySet):
454 return other
455 if isinstance(other, EmptyQuerySet):
456 return self
457 query = (
458 self
459 if self.query.can_filter()
460 else self.model._base_manager.filter(pk__in=self.values("pk"))
461 )
462 combined = query._chain()
463 combined._merge_known_related_objects(other)
464 if not other.query.can_filter():
465 other = other.model._base_manager.filter(pk__in=other.values("pk"))
466 combined.query.combine(other.query, sql.XOR)
467 return combined
468
469 ####################################
470 # METHODS THAT DO DATABASE QUERIES #
471 ####################################
472
473 def _iterator(self, use_chunked_fetch, chunk_size):
474 iterable = self._iterable_class(
475 self,
476 chunked_fetch=use_chunked_fetch,
477 chunk_size=chunk_size or 2000,
478 )
479 if not self._prefetch_related_lookups or chunk_size is None:
480 yield from iterable
481 return
482
483 iterator = iter(iterable)
484 while results := list(islice(iterator, chunk_size)):
485 prefetch_related_objects(results, *self._prefetch_related_lookups)
486 yield from results
487
488 def iterator(self, chunk_size=None):
489 """
490 An iterator over the results from applying this QuerySet to the
491 database. chunk_size must be provided for QuerySets that prefetch
492 related objects. Otherwise, a default chunk_size of 2000 is supplied.
493 """
494 if chunk_size is None:
495 if self._prefetch_related_lookups:
496 raise ValueError(
497 "chunk_size must be provided when using QuerySet.iterator() after "
498 "prefetch_related()."
499 )
500 elif chunk_size <= 0:
501 raise ValueError("Chunk size must be strictly positive.")
502 use_chunked_fetch = not connections[self.db].settings_dict.get(
503 "DISABLE_SERVER_SIDE_CURSORS"
504 )
505 return self._iterator(use_chunked_fetch, chunk_size)
506
507 def aggregate(self, *args, **kwargs):
508 """
509 Return a dictionary containing the calculations (aggregation)
510 over the current queryset.
511
512 If args is present the expression is passed as a kwarg using
513 the Aggregate object's default alias.
514 """
515 if self.query.distinct_fields:
516 raise NotImplementedError("aggregate() + distinct(fields) not implemented.")
517 self._validate_values_are_expressions(
518 (*args, *kwargs.values()), method_name="aggregate"
519 )
520 for arg in args:
521 # The default_alias property raises TypeError if default_alias
522 # can't be set automatically or AttributeError if it isn't an
523 # attribute.
524 try:
525 arg.default_alias
526 except (AttributeError, TypeError):
527 raise TypeError("Complex aggregates require an alias")
528 kwargs[arg.default_alias] = arg
529
530 return self.query.chain().get_aggregation(self.db, kwargs)
531
532 def count(self):
533 """
534 Perform a SELECT COUNT() and return the number of records as an
535 integer.
536
537 If the QuerySet is already fully cached, return the length of the
538 cached results set to avoid multiple SELECT COUNT(*) calls.
539 """
540 if self._result_cache is not None:
541 return len(self._result_cache)
542
543 return self.query.get_count(using=self.db)
544
545 def get(self, *args, **kwargs):
546 """
547 Perform the query and return a single object matching the given
548 keyword arguments.
549 """
550 if self.query.combinator and (args or kwargs):
551 raise NotSupportedError(
552 f"Calling QuerySet.get(...) with filters after {self.query.combinator}() is not "
553 "supported."
554 )
555 clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs)
556 if self.query.can_filter() and not self.query.distinct_fields:
557 clone = clone.order_by()
558 limit = None
559 if (
560 not clone.query.select_for_update
561 or connections[clone.db].features.supports_select_for_update_with_limit
562 ):
563 limit = MAX_GET_RESULTS
564 clone.query.set_limits(high=limit)
565 num = len(clone)
566 if num == 1:
567 return clone._result_cache[0]
568 if not num:
569 raise self.model.DoesNotExist(
570 f"{self.model._meta.object_name} matching query does not exist."
571 )
572 raise self.model.MultipleObjectsReturned(
573 "get() returned more than one {} -- it returned {}!".format(
574 self.model._meta.object_name,
575 num if not limit or num < limit else "more than %s" % (limit - 1),
576 )
577 )
578
579 def create(self, **kwargs):
580 """
581 Create a new object with the given kwargs, saving it to the database
582 and returning the created object.
583 """
584 obj = self.model(**kwargs)
585 self._for_write = True
586 obj.save(force_insert=True, using=self.db)
587 return obj
588
589 def _prepare_for_bulk_create(self, objs):
590 for obj in objs:
591 if obj.pk is None:
592 # Populate new PK values.
593 obj.pk = obj._meta.pk.get_pk_value_on_save(obj)
594 obj._prepare_related_fields_for_save(operation_name="bulk_create")
595
596 def _check_bulk_create_options(
597 self, update_conflicts, update_fields, unique_fields
598 ):
599 db_features = connections[self.db].features
600 if update_conflicts:
601 if not db_features.supports_update_conflicts:
602 raise NotSupportedError(
603 "This database backend does not support updating conflicts."
604 )
605 if not update_fields:
606 raise ValueError(
607 "Fields that will be updated when a row insertion fails "
608 "on conflicts must be provided."
609 )
610 if unique_fields and not db_features.supports_update_conflicts_with_target:
611 raise NotSupportedError(
612 "This database backend does not support updating "
613 "conflicts with specifying unique fields that can trigger "
614 "the upsert."
615 )
616 if not unique_fields and db_features.supports_update_conflicts_with_target:
617 raise ValueError(
618 "Unique fields that can trigger the upsert must be provided."
619 )
620 # Updating primary keys and non-concrete fields is forbidden.
621 if any(not f.concrete or f.many_to_many for f in update_fields):
622 raise ValueError(
623 "bulk_create() can only be used with concrete fields in "
624 "update_fields."
625 )
626 if any(f.primary_key for f in update_fields):
627 raise ValueError(
628 "bulk_create() cannot be used with primary keys in update_fields."
629 )
630 if unique_fields:
631 if any(not f.concrete or f.many_to_many for f in unique_fields):
632 raise ValueError(
633 "bulk_create() can only be used with concrete fields "
634 "in unique_fields."
635 )
636 return OnConflict.UPDATE
637 return None
638
639 def bulk_create(
640 self,
641 objs,
642 batch_size=None,
643 update_conflicts=False,
644 update_fields=None,
645 unique_fields=None,
646 ):
647 """
648 Insert each of the instances into the database. Do *not* call
649 save() on each of the instances, and do not set the primary key attribute if it is an
650 autoincrement field (except if features.can_return_rows_from_bulk_insert=True).
651 Multi-table models are not supported.
652 """
653 # When you bulk insert you don't get the primary keys back (if it's an
654 # autoincrement, except if can_return_rows_from_bulk_insert=True), so
655 # you can't insert into the child tables which references this. There
656 # are two workarounds:
657 # 1) This could be implemented if you didn't have an autoincrement pk
658 # 2) You could do it by doing O(n) normal inserts into the parent
659 # tables to get the primary keys back and then doing a single bulk
660 # insert into the childmost table.
661 # We currently set the primary keys on the objects when using
662 # PostgreSQL via the RETURNING ID clause. It should be possible for
663 # Oracle as well, but the semantics for extracting the primary keys is
664 # trickier so it's not done yet.
665 if batch_size is not None and batch_size <= 0:
666 raise ValueError("Batch size must be a positive integer.")
667
668 if not objs:
669 return objs
670 opts = self.model._meta
671 if unique_fields:
672 # Primary key is allowed in unique_fields.
673 unique_fields = [
674 self.model._meta.get_field(opts.pk.name if name == "pk" else name)
675 for name in unique_fields
676 ]
677 if update_fields:
678 update_fields = [self.model._meta.get_field(name) for name in update_fields]
679 on_conflict = self._check_bulk_create_options(
680 update_conflicts,
681 update_fields,
682 unique_fields,
683 )
684 self._for_write = True
685 fields = opts.concrete_fields
686 objs = list(objs)
687 self._prepare_for_bulk_create(objs)
688 with transaction.atomic(using=self.db, savepoint=False):
689 objs_with_pk, objs_without_pk = partition(lambda o: o.pk is None, objs)
690 if objs_with_pk:
691 returned_columns = self._batched_insert(
692 objs_with_pk,
693 fields,
694 batch_size,
695 on_conflict=on_conflict,
696 update_fields=update_fields,
697 unique_fields=unique_fields,
698 )
699 for obj_with_pk, results in zip(objs_with_pk, returned_columns):
700 for result, field in zip(results, opts.db_returning_fields):
701 if field != opts.pk:
702 setattr(obj_with_pk, field.attname, result)
703 for obj_with_pk in objs_with_pk:
704 obj_with_pk._state.adding = False
705 obj_with_pk._state.db = self.db
706 if objs_without_pk:
707 fields = [f for f in fields if not isinstance(f, AutoField)]
708 returned_columns = self._batched_insert(
709 objs_without_pk,
710 fields,
711 batch_size,
712 on_conflict=on_conflict,
713 update_fields=update_fields,
714 unique_fields=unique_fields,
715 )
716 connection = connections[self.db]
717 if (
718 connection.features.can_return_rows_from_bulk_insert
719 and on_conflict is None
720 ):
721 assert len(returned_columns) == len(objs_without_pk)
722 for obj_without_pk, results in zip(objs_without_pk, returned_columns):
723 for result, field in zip(results, opts.db_returning_fields):
724 setattr(obj_without_pk, field.attname, result)
725 obj_without_pk._state.adding = False
726 obj_without_pk._state.db = self.db
727
728 return objs
729
730 def bulk_update(self, objs, fields, batch_size=None):
731 """
732 Update the given fields in each of the given objects in the database.
733 """
734 if batch_size is not None and batch_size <= 0:
735 raise ValueError("Batch size must be a positive integer.")
736 if not fields:
737 raise ValueError("Field names must be given to bulk_update().")
738 objs = tuple(objs)
739 if any(obj.pk is None for obj in objs):
740 raise ValueError("All bulk_update() objects must have a primary key set.")
741 fields = [self.model._meta.get_field(name) for name in fields]
742 if any(not f.concrete or f.many_to_many for f in fields):
743 raise ValueError("bulk_update() can only be used with concrete fields.")
744 if any(f.primary_key for f in fields):
745 raise ValueError("bulk_update() cannot be used with primary key fields.")
746 if not objs:
747 return 0
748 for obj in objs:
749 obj._prepare_related_fields_for_save(
750 operation_name="bulk_update", fields=fields
751 )
752 # PK is used twice in the resulting update query, once in the filter
753 # and once in the WHEN. Each field will also have one CAST.
754 self._for_write = True
755 connection = connections[self.db]
756 max_batch_size = connection.ops.bulk_batch_size(["pk", "pk"] + fields, objs)
757 batch_size = min(batch_size, max_batch_size) if batch_size else max_batch_size
758 requires_casting = connection.features.requires_casted_case_in_updates
759 batches = (objs[i : i + batch_size] for i in range(0, len(objs), batch_size))
760 updates = []
761 for batch_objs in batches:
762 update_kwargs = {}
763 for field in fields:
764 when_statements = []
765 for obj in batch_objs:
766 attr = getattr(obj, field.attname)
767 if not hasattr(attr, "resolve_expression"):
768 attr = Value(attr, output_field=field)
769 when_statements.append(When(pk=obj.pk, then=attr))
770 case_statement = Case(*when_statements, output_field=field)
771 if requires_casting:
772 case_statement = Cast(case_statement, output_field=field)
773 update_kwargs[field.attname] = case_statement
774 updates.append(([obj.pk for obj in batch_objs], update_kwargs))
775 rows_updated = 0
776 queryset = self.using(self.db)
777 with transaction.atomic(using=self.db, savepoint=False):
778 for pks, update_kwargs in updates:
779 rows_updated += queryset.filter(pk__in=pks).update(**update_kwargs)
780 return rows_updated
781
782 def get_or_create(self, defaults=None, **kwargs):
783 """
784 Look up an object with the given kwargs, creating one if necessary.
785 Return a tuple of (object, created), where created is a boolean
786 specifying whether an object was created.
787 """
788 # The get() needs to be targeted at the write database in order
789 # to avoid potential transaction consistency problems.
790 self._for_write = True
791 try:
792 return self.get(**kwargs), False
793 except self.model.DoesNotExist:
794 params = self._extract_model_params(defaults, **kwargs)
795 # Try to create an object using passed params.
796 try:
797 with transaction.atomic(using=self.db):
798 params = dict(resolve_callables(params))
799 return self.create(**params), True
800 except (IntegrityError, ValidationError):
801 # Since create() also validates by default,
802 # we can get any kind of ValidationError here,
803 # or it can flow through and get an IntegrityError from the database.
804 # The main thing we're concerned about is uniqueness failures,
805 # but ValidationError could include other things too.
806 # In all cases though it should be fine to try the get() again
807 # and return an existing object.
808 try:
809 return self.get(**kwargs), False
810 except self.model.DoesNotExist:
811 pass
812 raise
813
814 def update_or_create(self, defaults=None, create_defaults=None, **kwargs):
815 """
816 Look up an object with the given kwargs, updating one with defaults
817 if it exists, otherwise create a new one. Optionally, an object can
818 be created with different values than defaults by using
819 create_defaults.
820 Return a tuple (object, created), where created is a boolean
821 specifying whether an object was created.
822 """
823 if create_defaults is None:
824 update_defaults = create_defaults = defaults or {}
825 else:
826 update_defaults = defaults or {}
827 self._for_write = True
828 with transaction.atomic(using=self.db):
829 # Lock the row so that a concurrent update is blocked until
830 # update_or_create() has performed its save.
831 obj, created = self.select_for_update().get_or_create(
832 create_defaults, **kwargs
833 )
834 if created:
835 return obj, created
836 for k, v in resolve_callables(update_defaults):
837 setattr(obj, k, v)
838
839 update_fields = set(update_defaults)
840 concrete_field_names = self.model._meta._non_pk_concrete_field_names
841 # update_fields does not support non-concrete fields.
842 if concrete_field_names.issuperset(update_fields):
843 # Add fields which are set on pre_save(), e.g. auto_now fields.
844 # This is to maintain backward compatibility as these fields
845 # are not updated unless explicitly specified in the
846 # update_fields list.
847 for field in self.model._meta.local_concrete_fields:
848 if not (
849 field.primary_key or field.__class__.pre_save is Field.pre_save
850 ):
851 update_fields.add(field.name)
852 if field.name != field.attname:
853 update_fields.add(field.attname)
854 obj.save(using=self.db, update_fields=update_fields)
855 else:
856 obj.save(using=self.db)
857 return obj, False
858
859 def _extract_model_params(self, defaults, **kwargs):
860 """
861 Prepare `params` for creating a model instance based on the given
862 kwargs; for use by get_or_create().
863 """
864 defaults = defaults or {}
865 params = {k: v for k, v in kwargs.items() if LOOKUP_SEP not in k}
866 params.update(defaults)
867 property_names = self.model._meta._property_names
868 invalid_params = []
869 for param in params:
870 try:
871 self.model._meta.get_field(param)
872 except exceptions.FieldDoesNotExist:
873 # It's okay to use a model's property if it has a setter.
874 if not (param in property_names and getattr(self.model, param).fset):
875 invalid_params.append(param)
876 if invalid_params:
877 raise exceptions.FieldError(
878 "Invalid field name(s) for model {}: '{}'.".format(
879 self.model._meta.object_name,
880 "', '".join(sorted(invalid_params)),
881 )
882 )
883 return params
884
885 def _earliest(self, *fields):
886 """
887 Return the earliest object according to fields (if given) or by the
888 model's Meta.get_latest_by.
889 """
890 if fields:
891 order_by = fields
892 else:
893 order_by = getattr(self.model._meta, "get_latest_by")
894 if order_by and not isinstance(order_by, tuple | list):
895 order_by = (order_by,)
896 if order_by is None:
897 raise ValueError(
898 "earliest() and latest() require either fields as positional "
899 "arguments or 'get_latest_by' in the model's Meta."
900 )
901 obj = self._chain()
902 obj.query.set_limits(high=1)
903 obj.query.clear_ordering(force=True)
904 obj.query.add_ordering(*order_by)
905 return obj.get()
906
907 def earliest(self, *fields):
908 if self.query.is_sliced:
909 raise TypeError("Cannot change a query once a slice has been taken.")
910 return self._earliest(*fields)
911
912 def latest(self, *fields):
913 """
914 Return the latest object according to fields (if given) or by the
915 model's Meta.get_latest_by.
916 """
917 if self.query.is_sliced:
918 raise TypeError("Cannot change a query once a slice has been taken.")
919 return self.reverse()._earliest(*fields)
920
921 def first(self):
922 """Return the first object of a query or None if no match is found."""
923 if self.ordered:
924 queryset = self
925 else:
926 self._check_ordering_first_last_queryset_aggregation(method="first")
927 queryset = self.order_by("pk")
928 for obj in queryset[:1]:
929 return obj
930
931 def last(self):
932 """Return the last object of a query or None if no match is found."""
933 if self.ordered:
934 queryset = self.reverse()
935 else:
936 self._check_ordering_first_last_queryset_aggregation(method="last")
937 queryset = self.order_by("-pk")
938 for obj in queryset[:1]:
939 return obj
940
941 def in_bulk(self, id_list=None, *, field_name="pk"):
942 """
943 Return a dictionary mapping each of the given IDs to the object with
944 that ID. If `id_list` isn't provided, evaluate the entire QuerySet.
945 """
946 if self.query.is_sliced:
947 raise TypeError("Cannot use 'limit' or 'offset' with in_bulk().")
948 opts = self.model._meta
949 unique_fields = [
950 constraint.fields[0]
951 for constraint in opts.total_unique_constraints
952 if len(constraint.fields) == 1
953 ]
954 if (
955 field_name != "pk"
956 and not opts.get_field(field_name).primary_key
957 and field_name not in unique_fields
958 and self.query.distinct_fields != (field_name,)
959 ):
960 raise ValueError(
961 f"in_bulk()'s field_name must be a unique field but {field_name!r} isn't."
962 )
963 if id_list is not None:
964 if not id_list:
965 return {}
966 filter_key = f"{field_name}__in"
967 batch_size = connections[self.db].features.max_query_params
968 id_list = tuple(id_list)
969 # If the database has a limit on the number of query parameters
970 # (e.g. SQLite), retrieve objects in batches if necessary.
971 if batch_size and batch_size < len(id_list):
972 qs = ()
973 for offset in range(0, len(id_list), batch_size):
974 batch = id_list[offset : offset + batch_size]
975 qs += tuple(self.filter(**{filter_key: batch}))
976 else:
977 qs = self.filter(**{filter_key: id_list})
978 else:
979 qs = self._chain()
980 return {getattr(obj, field_name): obj for obj in qs}
981
982 def delete(self):
983 """Delete the records in the current QuerySet."""
984 self._not_support_combined_queries("delete")
985 if self.query.is_sliced:
986 raise TypeError("Cannot use 'limit' or 'offset' with delete().")
987 if self.query.distinct or self.query.distinct_fields:
988 raise TypeError("Cannot call delete() after .distinct().")
989 if self._fields is not None:
990 raise TypeError("Cannot call delete() after .values() or .values_list()")
991
992 del_query = self._chain()
993
994 # The delete is actually 2 queries - one to find related objects,
995 # and one to delete. Make sure that the discovery of related
996 # objects is performed on the same database as the deletion.
997 del_query._for_write = True
998
999 # Disable non-supported fields.
1000 del_query.query.select_for_update = False
1001 del_query.query.select_related = False
1002 del_query.query.clear_ordering(force=True)
1003
1004 from plain.models.deletion import Collector
1005
1006 collector = Collector(using=del_query.db, origin=self)
1007 collector.collect(del_query)
1008 deleted, _rows_count = collector.delete()
1009
1010 # Clear the result cache, in case this QuerySet gets reused.
1011 self._result_cache = None
1012 return deleted, _rows_count
1013
1014 delete.queryset_only = True
1015
1016 def _raw_delete(self, using):
1017 """
1018 Delete objects found from the given queryset in single direct SQL
1019 query. No signals are sent and there is no protection for cascades.
1020 """
1021 query = self.query.clone()
1022 query.__class__ = sql.DeleteQuery
1023 cursor = query.get_compiler(using).execute_sql(CURSOR)
1024 if cursor:
1025 with cursor:
1026 return cursor.rowcount
1027 return 0
1028
1029 def update(self, **kwargs):
1030 """
1031 Update all elements in the current QuerySet, setting all the given
1032 fields to the appropriate values.
1033 """
1034 self._not_support_combined_queries("update")
1035 if self.query.is_sliced:
1036 raise TypeError("Cannot update a query once a slice has been taken.")
1037 self._for_write = True
1038 query = self.query.chain(sql.UpdateQuery)
1039 query.add_update_values(kwargs)
1040
1041 # Inline annotations in order_by(), if possible.
1042 new_order_by = []
1043 for col in query.order_by:
1044 alias = col
1045 descending = False
1046 if isinstance(alias, str) and alias.startswith("-"):
1047 alias = alias.removeprefix("-")
1048 descending = True
1049 if annotation := query.annotations.get(alias):
1050 if getattr(annotation, "contains_aggregate", False):
1051 raise exceptions.FieldError(
1052 f"Cannot update when ordering by an aggregate: {annotation}"
1053 )
1054 if descending:
1055 annotation = annotation.desc()
1056 new_order_by.append(annotation)
1057 else:
1058 new_order_by.append(col)
1059 query.order_by = tuple(new_order_by)
1060
1061 # Clear any annotations so that they won't be present in subqueries.
1062 query.annotations = {}
1063 with transaction.mark_for_rollback_on_error(using=self.db):
1064 rows = query.get_compiler(self.db).execute_sql(CURSOR)
1065 self._result_cache = None
1066 return rows
1067
1068 def _update(self, values):
1069 """
1070 A version of update() that accepts field objects instead of field names.
1071 Used primarily for model saving and not intended for use by general
1072 code (it requires too much poking around at model internals to be
1073 useful at that level).
1074 """
1075 if self.query.is_sliced:
1076 raise TypeError("Cannot update a query once a slice has been taken.")
1077 query = self.query.chain(sql.UpdateQuery)
1078 query.add_update_fields(values)
1079 # Clear any annotations so that they won't be present in subqueries.
1080 query.annotations = {}
1081 self._result_cache = None
1082 return query.get_compiler(self.db).execute_sql(CURSOR)
1083
1084 _update.queryset_only = False
1085
1086 def exists(self):
1087 """
1088 Return True if the QuerySet would have any results, False otherwise.
1089 """
1090 if self._result_cache is None:
1091 return self.query.has_results(using=self.db)
1092 return bool(self._result_cache)
1093
1094 def contains(self, obj):
1095 """
1096 Return True if the QuerySet contains the provided obj,
1097 False otherwise.
1098 """
1099 self._not_support_combined_queries("contains")
1100 if self._fields is not None:
1101 raise TypeError(
1102 "Cannot call QuerySet.contains() after .values() or .values_list()."
1103 )
1104 try:
1105 if obj._meta.concrete_model != self.model._meta.concrete_model:
1106 return False
1107 except AttributeError:
1108 raise TypeError("'obj' must be a model instance.")
1109 if obj.pk is None:
1110 raise ValueError("QuerySet.contains() cannot be used on unsaved objects.")
1111 if self._result_cache is not None:
1112 return obj in self._result_cache
1113 return self.filter(pk=obj.pk).exists()
1114
1115 def _prefetch_related_objects(self):
1116 # This method can only be called once the result cache has been filled.
1117 prefetch_related_objects(self._result_cache, *self._prefetch_related_lookups)
1118 self._prefetch_done = True
1119
1120 def explain(self, *, format=None, **options):
1121 """
1122 Runs an EXPLAIN on the SQL query this QuerySet would perform, and
1123 returns the results.
1124 """
1125 return self.query.explain(using=self.db, format=format, **options)
1126
1127 ##################################################
1128 # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
1129 ##################################################
1130
1131 def raw(self, raw_query, params=(), translations=None, using=None):
1132 if using is None:
1133 using = self.db
1134 qs = RawQuerySet(
1135 raw_query,
1136 model=self.model,
1137 params=params,
1138 translations=translations,
1139 using=using,
1140 )
1141 qs._prefetch_related_lookups = self._prefetch_related_lookups[:]
1142 return qs
1143
1144 def _values(self, *fields, **expressions):
1145 clone = self._chain()
1146 if expressions:
1147 clone = clone.annotate(**expressions)
1148 clone._fields = fields
1149 clone.query.set_values(fields)
1150 return clone
1151
1152 def values(self, *fields, **expressions):
1153 fields += tuple(expressions)
1154 clone = self._values(*fields, **expressions)
1155 clone._iterable_class = ValuesIterable
1156 return clone
1157
1158 def values_list(self, *fields, flat=False, named=False):
1159 if flat and named:
1160 raise TypeError("'flat' and 'named' can't be used together.")
1161 if flat and len(fields) > 1:
1162 raise TypeError(
1163 "'flat' is not valid when values_list is called with more than one "
1164 "field."
1165 )
1166
1167 field_names = {f for f in fields if not hasattr(f, "resolve_expression")}
1168 _fields = []
1169 expressions = {}
1170 counter = 1
1171 for field in fields:
1172 if hasattr(field, "resolve_expression"):
1173 field_id_prefix = getattr(
1174 field, "default_alias", field.__class__.__name__.lower()
1175 )
1176 while True:
1177 field_id = field_id_prefix + str(counter)
1178 counter += 1
1179 if field_id not in field_names:
1180 break
1181 expressions[field_id] = field
1182 _fields.append(field_id)
1183 else:
1184 _fields.append(field)
1185
1186 clone = self._values(*_fields, **expressions)
1187 clone._iterable_class = (
1188 NamedValuesListIterable
1189 if named
1190 else FlatValuesListIterable
1191 if flat
1192 else ValuesListIterable
1193 )
1194 return clone
1195
1196 def dates(self, field_name, kind, order="ASC"):
1197 """
1198 Return a list of date objects representing all available dates for
1199 the given field_name, scoped to 'kind'.
1200 """
1201 if kind not in ("year", "month", "week", "day"):
1202 raise ValueError("'kind' must be one of 'year', 'month', 'week', or 'day'.")
1203 if order not in ("ASC", "DESC"):
1204 raise ValueError("'order' must be either 'ASC' or 'DESC'.")
1205 return (
1206 self.annotate(
1207 datefield=Trunc(field_name, kind, output_field=DateField()),
1208 plain_field=F(field_name),
1209 )
1210 .values_list("datefield", flat=True)
1211 .distinct()
1212 .filter(plain_field__isnull=False)
1213 .order_by(("-" if order == "DESC" else "") + "datefield")
1214 )
1215
1216 def datetimes(self, field_name, kind, order="ASC", tzinfo=None):
1217 """
1218 Return a list of datetime objects representing all available
1219 datetimes for the given field_name, scoped to 'kind'.
1220 """
1221 if kind not in ("year", "month", "week", "day", "hour", "minute", "second"):
1222 raise ValueError(
1223 "'kind' must be one of 'year', 'month', 'week', 'day', "
1224 "'hour', 'minute', or 'second'."
1225 )
1226 if order not in ("ASC", "DESC"):
1227 raise ValueError("'order' must be either 'ASC' or 'DESC'.")
1228
1229 if tzinfo is None:
1230 tzinfo = timezone.get_current_timezone()
1231
1232 return (
1233 self.annotate(
1234 datetimefield=Trunc(
1235 field_name,
1236 kind,
1237 output_field=DateTimeField(),
1238 tzinfo=tzinfo,
1239 ),
1240 plain_field=F(field_name),
1241 )
1242 .values_list("datetimefield", flat=True)
1243 .distinct()
1244 .filter(plain_field__isnull=False)
1245 .order_by(("-" if order == "DESC" else "") + "datetimefield")
1246 )
1247
1248 def none(self):
1249 """Return an empty QuerySet."""
1250 clone = self._chain()
1251 clone.query.set_empty()
1252 return clone
1253
1254 ##################################################################
1255 # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
1256 ##################################################################
1257
1258 def all(self):
1259 """
1260 Return a new QuerySet that is a copy of the current one. This allows a
1261 QuerySet to proxy for a model manager in some cases.
1262 """
1263 return self._chain()
1264
1265 def filter(self, *args, **kwargs):
1266 """
1267 Return a new QuerySet instance with the args ANDed to the existing
1268 set.
1269 """
1270 self._not_support_combined_queries("filter")
1271 return self._filter_or_exclude(False, args, kwargs)
1272
1273 def exclude(self, *args, **kwargs):
1274 """
1275 Return a new QuerySet instance with NOT (args) ANDed to the existing
1276 set.
1277 """
1278 self._not_support_combined_queries("exclude")
1279 return self._filter_or_exclude(True, args, kwargs)
1280
1281 def _filter_or_exclude(self, negate, args, kwargs):
1282 if (args or kwargs) and self.query.is_sliced:
1283 raise TypeError("Cannot filter a query once a slice has been taken.")
1284 clone = self._chain()
1285 if self._defer_next_filter:
1286 self._defer_next_filter = False
1287 clone._deferred_filter = negate, args, kwargs
1288 else:
1289 clone._filter_or_exclude_inplace(negate, args, kwargs)
1290 return clone
1291
1292 def _filter_or_exclude_inplace(self, negate, args, kwargs):
1293 if negate:
1294 self._query.add_q(~Q(*args, **kwargs))
1295 else:
1296 self._query.add_q(Q(*args, **kwargs))
1297
1298 def complex_filter(self, filter_obj):
1299 """
1300 Return a new QuerySet instance with filter_obj added to the filters.
1301
1302 filter_obj can be a Q object or a dictionary of keyword lookup
1303 arguments.
1304
1305 This exists to support framework features such as 'limit_choices_to',
1306 and usually it will be more natural to use other methods.
1307 """
1308 if isinstance(filter_obj, Q):
1309 clone = self._chain()
1310 clone.query.add_q(filter_obj)
1311 return clone
1312 else:
1313 return self._filter_or_exclude(False, args=(), kwargs=filter_obj)
1314
1315 def _combinator_query(self, combinator, *other_qs, all=False):
1316 # Clone the query to inherit the select list and everything
1317 clone = self._chain()
1318 # Clear limits and ordering so they can be reapplied
1319 clone.query.clear_ordering(force=True)
1320 clone.query.clear_limits()
1321 clone.query.combined_queries = (self.query,) + tuple(
1322 qs.query for qs in other_qs
1323 )
1324 clone.query.combinator = combinator
1325 clone.query.combinator_all = all
1326 return clone
1327
1328 def union(self, *other_qs, all=False):
1329 # If the query is an EmptyQuerySet, combine all nonempty querysets.
1330 if isinstance(self, EmptyQuerySet):
1331 qs = [q for q in other_qs if not isinstance(q, EmptyQuerySet)]
1332 if not qs:
1333 return self
1334 if len(qs) == 1:
1335 return qs[0]
1336 return qs[0]._combinator_query("union", *qs[1:], all=all)
1337 return self._combinator_query("union", *other_qs, all=all)
1338
1339 def intersection(self, *other_qs):
1340 # If any query is an EmptyQuerySet, return it.
1341 if isinstance(self, EmptyQuerySet):
1342 return self
1343 for other in other_qs:
1344 if isinstance(other, EmptyQuerySet):
1345 return other
1346 return self._combinator_query("intersection", *other_qs)
1347
1348 def difference(self, *other_qs):
1349 # If the query is an EmptyQuerySet, return it.
1350 if isinstance(self, EmptyQuerySet):
1351 return self
1352 return self._combinator_query("difference", *other_qs)
1353
1354 def select_for_update(self, nowait=False, skip_locked=False, of=(), no_key=False):
1355 """
1356 Return a new QuerySet instance that will select objects with a
1357 FOR UPDATE lock.
1358 """
1359 if nowait and skip_locked:
1360 raise ValueError("The nowait option cannot be used with skip_locked.")
1361 obj = self._chain()
1362 obj._for_write = True
1363 obj.query.select_for_update = True
1364 obj.query.select_for_update_nowait = nowait
1365 obj.query.select_for_update_skip_locked = skip_locked
1366 obj.query.select_for_update_of = of
1367 obj.query.select_for_no_key_update = no_key
1368 return obj
1369
1370 def select_related(self, *fields):
1371 """
1372 Return a new QuerySet instance that will select related objects.
1373
1374 If fields are specified, they must be ForeignKey fields and only those
1375 related objects are included in the selection.
1376
1377 If select_related(None) is called, clear the list.
1378 """
1379 self._not_support_combined_queries("select_related")
1380 if self._fields is not None:
1381 raise TypeError(
1382 "Cannot call select_related() after .values() or .values_list()"
1383 )
1384
1385 obj = self._chain()
1386 if fields == (None,):
1387 obj.query.select_related = False
1388 elif fields:
1389 obj.query.add_select_related(fields)
1390 else:
1391 obj.query.select_related = True
1392 return obj
1393
1394 def prefetch_related(self, *lookups):
1395 """
1396 Return a new QuerySet instance that will prefetch the specified
1397 Many-To-One and Many-To-Many related objects when the QuerySet is
1398 evaluated.
1399
1400 When prefetch_related() is called more than once, append to the list of
1401 prefetch lookups. If prefetch_related(None) is called, clear the list.
1402 """
1403 self._not_support_combined_queries("prefetch_related")
1404 clone = self._chain()
1405 if lookups == (None,):
1406 clone._prefetch_related_lookups = ()
1407 else:
1408 for lookup in lookups:
1409 if isinstance(lookup, Prefetch):
1410 lookup = lookup.prefetch_to
1411 lookup = lookup.split(LOOKUP_SEP, 1)[0]
1412 if lookup in self.query._filtered_relations:
1413 raise ValueError(
1414 "prefetch_related() is not supported with FilteredRelation."
1415 )
1416 clone._prefetch_related_lookups = clone._prefetch_related_lookups + lookups
1417 return clone
1418
1419 def annotate(self, *args, **kwargs):
1420 """
1421 Return a query set in which the returned objects have been annotated
1422 with extra data or aggregations.
1423 """
1424 self._not_support_combined_queries("annotate")
1425 return self._annotate(args, kwargs, select=True)
1426
1427 def alias(self, *args, **kwargs):
1428 """
1429 Return a query set with added aliases for extra data or aggregations.
1430 """
1431 self._not_support_combined_queries("alias")
1432 return self._annotate(args, kwargs, select=False)
1433
1434 def _annotate(self, args, kwargs, select=True):
1435 self._validate_values_are_expressions(
1436 args + tuple(kwargs.values()), method_name="annotate"
1437 )
1438 annotations = {}
1439 for arg in args:
1440 # The default_alias property may raise a TypeError.
1441 try:
1442 if arg.default_alias in kwargs:
1443 raise ValueError(
1444 f"The named annotation '{arg.default_alias}' conflicts with the "
1445 "default name for another annotation."
1446 )
1447 except TypeError:
1448 raise TypeError("Complex annotations require an alias")
1449 annotations[arg.default_alias] = arg
1450 annotations.update(kwargs)
1451
1452 clone = self._chain()
1453 names = self._fields
1454 if names is None:
1455 names = set(
1456 chain.from_iterable(
1457 (field.name, field.attname)
1458 if hasattr(field, "attname")
1459 else (field.name,)
1460 for field in self.model._meta.get_fields()
1461 )
1462 )
1463
1464 for alias, annotation in annotations.items():
1465 if alias in names:
1466 raise ValueError(
1467 f"The annotation '{alias}' conflicts with a field on the model."
1468 )
1469 if isinstance(annotation, FilteredRelation):
1470 clone.query.add_filtered_relation(annotation, alias)
1471 else:
1472 clone.query.add_annotation(
1473 annotation,
1474 alias,
1475 select=select,
1476 )
1477 for alias, annotation in clone.query.annotations.items():
1478 if alias in annotations and annotation.contains_aggregate:
1479 if clone._fields is None:
1480 clone.query.group_by = True
1481 else:
1482 clone.query.set_group_by()
1483 break
1484
1485 return clone
1486
1487 def order_by(self, *field_names):
1488 """Return a new QuerySet instance with the ordering changed."""
1489 if self.query.is_sliced:
1490 raise TypeError("Cannot reorder a query once a slice has been taken.")
1491 obj = self._chain()
1492 obj.query.clear_ordering(force=True, clear_default=False)
1493 obj.query.add_ordering(*field_names)
1494 return obj
1495
1496 def distinct(self, *field_names):
1497 """
1498 Return a new QuerySet instance that will select only distinct results.
1499 """
1500 self._not_support_combined_queries("distinct")
1501 if self.query.is_sliced:
1502 raise TypeError(
1503 "Cannot create distinct fields once a slice has been taken."
1504 )
1505 obj = self._chain()
1506 obj.query.add_distinct_fields(*field_names)
1507 return obj
1508
1509 def extra(
1510 self,
1511 select=None,
1512 where=None,
1513 params=None,
1514 tables=None,
1515 order_by=None,
1516 select_params=None,
1517 ):
1518 """Add extra SQL fragments to the query."""
1519 self._not_support_combined_queries("extra")
1520 if self.query.is_sliced:
1521 raise TypeError("Cannot change a query once a slice has been taken.")
1522 clone = self._chain()
1523 clone.query.add_extra(select, select_params, where, params, tables, order_by)
1524 return clone
1525
1526 def reverse(self):
1527 """Reverse the ordering of the QuerySet."""
1528 if self.query.is_sliced:
1529 raise TypeError("Cannot reverse a query once a slice has been taken.")
1530 clone = self._chain()
1531 clone.query.standard_ordering = not clone.query.standard_ordering
1532 return clone
1533
1534 def defer(self, *fields):
1535 """
1536 Defer the loading of data for certain fields until they are accessed.
1537 Add the set of deferred fields to any existing set of deferred fields.
1538 The only exception to this is if None is passed in as the only
1539 parameter, in which case removal all deferrals.
1540 """
1541 self._not_support_combined_queries("defer")
1542 if self._fields is not None:
1543 raise TypeError("Cannot call defer() after .values() or .values_list()")
1544 clone = self._chain()
1545 if fields == (None,):
1546 clone.query.clear_deferred_loading()
1547 else:
1548 clone.query.add_deferred_loading(fields)
1549 return clone
1550
1551 def only(self, *fields):
1552 """
1553 Essentially, the opposite of defer(). Only the fields passed into this
1554 method and that are not already specified as deferred are loaded
1555 immediately when the queryset is evaluated.
1556 """
1557 self._not_support_combined_queries("only")
1558 if self._fields is not None:
1559 raise TypeError("Cannot call only() after .values() or .values_list()")
1560 if fields == (None,):
1561 # Can only pass None to defer(), not only(), as the rest option.
1562 # That won't stop people trying to do this, so let's be explicit.
1563 raise TypeError("Cannot pass None as an argument to only().")
1564 for field in fields:
1565 field = field.split(LOOKUP_SEP, 1)[0]
1566 if field in self.query._filtered_relations:
1567 raise ValueError("only() is not supported with FilteredRelation.")
1568 clone = self._chain()
1569 clone.query.add_immediate_loading(fields)
1570 return clone
1571
1572 def using(self, alias):
1573 """Select which database this QuerySet should execute against."""
1574 clone = self._chain()
1575 clone._db = alias
1576 return clone
1577
1578 ###################################
1579 # PUBLIC INTROSPECTION ATTRIBUTES #
1580 ###################################
1581
1582 @property
1583 def ordered(self):
1584 """
1585 Return True if the QuerySet is ordered -- i.e. has an order_by()
1586 clause or a default ordering on the model (or is empty).
1587 """
1588 if isinstance(self, EmptyQuerySet):
1589 return True
1590 if self.query.extra_order_by or self.query.order_by:
1591 return True
1592 elif (
1593 self.query.default_ordering
1594 and self.query.get_meta().ordering
1595 and
1596 # A default ordering doesn't affect GROUP BY queries.
1597 not self.query.group_by
1598 ):
1599 return True
1600 else:
1601 return False
1602
1603 @property
1604 def db(self):
1605 """Return the database used if this query is executed now."""
1606 if self._for_write:
1607 return self._db or router.db_for_write(self.model, **self._hints)
1608 return self._db or router.db_for_read(self.model, **self._hints)
1609
1610 ###################
1611 # PRIVATE METHODS #
1612 ###################
1613
1614 def _insert(
1615 self,
1616 objs,
1617 fields,
1618 returning_fields=None,
1619 raw=False,
1620 using=None,
1621 on_conflict=None,
1622 update_fields=None,
1623 unique_fields=None,
1624 ):
1625 """
1626 Insert a new record for the given model. This provides an interface to
1627 the InsertQuery class and is how Model.save() is implemented.
1628 """
1629 self._for_write = True
1630 if using is None:
1631 using = self.db
1632 query = sql.InsertQuery(
1633 self.model,
1634 on_conflict=on_conflict,
1635 update_fields=update_fields,
1636 unique_fields=unique_fields,
1637 )
1638 query.insert_values(fields, objs, raw=raw)
1639 return query.get_compiler(using=using).execute_sql(returning_fields)
1640
1641 _insert.queryset_only = False
1642
1643 def _batched_insert(
1644 self,
1645 objs,
1646 fields,
1647 batch_size,
1648 on_conflict=None,
1649 update_fields=None,
1650 unique_fields=None,
1651 ):
1652 """
1653 Helper method for bulk_create() to insert objs one batch at a time.
1654 """
1655 connection = connections[self.db]
1656 ops = connection.ops
1657 max_batch_size = max(ops.bulk_batch_size(fields, objs), 1)
1658 batch_size = min(batch_size, max_batch_size) if batch_size else max_batch_size
1659 inserted_rows = []
1660 bulk_return = connection.features.can_return_rows_from_bulk_insert
1661 for item in [objs[i : i + batch_size] for i in range(0, len(objs), batch_size)]:
1662 if bulk_return and on_conflict is None:
1663 inserted_rows.extend(
1664 self._insert(
1665 item,
1666 fields=fields,
1667 using=self.db,
1668 returning_fields=self.model._meta.db_returning_fields,
1669 )
1670 )
1671 else:
1672 self._insert(
1673 item,
1674 fields=fields,
1675 using=self.db,
1676 on_conflict=on_conflict,
1677 update_fields=update_fields,
1678 unique_fields=unique_fields,
1679 )
1680 return inserted_rows
1681
1682 def _chain(self):
1683 """
1684 Return a copy of the current QuerySet that's ready for another
1685 operation.
1686 """
1687 obj = self._clone()
1688 if obj._sticky_filter:
1689 obj.query.filter_is_sticky = True
1690 obj._sticky_filter = False
1691 return obj
1692
1693 def _clone(self):
1694 """
1695 Return a copy of the current QuerySet. A lightweight alternative
1696 to deepcopy().
1697 """
1698 c = self.__class__(
1699 model=self.model,
1700 query=self.query.chain(),
1701 using=self._db,
1702 hints=self._hints,
1703 )
1704 c._sticky_filter = self._sticky_filter
1705 c._for_write = self._for_write
1706 c._prefetch_related_lookups = self._prefetch_related_lookups[:]
1707 c._known_related_objects = self._known_related_objects
1708 c._iterable_class = self._iterable_class
1709 c._fields = self._fields
1710 return c
1711
1712 def _fetch_all(self):
1713 if self._result_cache is None:
1714 self._result_cache = list(self._iterable_class(self))
1715 if self._prefetch_related_lookups and not self._prefetch_done:
1716 self._prefetch_related_objects()
1717
1718 def _next_is_sticky(self):
1719 """
1720 Indicate that the next filter call and the one following that should
1721 be treated as a single filter. This is only important when it comes to
1722 determining when to reuse tables for many-to-many filters. Required so
1723 that we can filter naturally on the results of related managers.
1724
1725 This doesn't return a clone of the current QuerySet (it returns
1726 "self"). The method is only used internally and should be immediately
1727 followed by a filter() that does create a clone.
1728 """
1729 self._sticky_filter = True
1730 return self
1731
1732 def _merge_sanity_check(self, other):
1733 """Check that two QuerySet classes may be merged."""
1734 if self._fields is not None and (
1735 set(self.query.values_select) != set(other.query.values_select)
1736 or set(self.query.extra_select) != set(other.query.extra_select)
1737 or set(self.query.annotation_select) != set(other.query.annotation_select)
1738 ):
1739 raise TypeError(
1740 f"Merging '{self.__class__.__name__}' classes must involve the same values in each case."
1741 )
1742
1743 def _merge_known_related_objects(self, other):
1744 """
1745 Keep track of all known related objects from either QuerySet instance.
1746 """
1747 for field, objects in other._known_related_objects.items():
1748 self._known_related_objects.setdefault(field, {}).update(objects)
1749
1750 def resolve_expression(self, *args, **kwargs):
1751 if self._fields and len(self._fields) > 1:
1752 # values() queryset can only be used as nested queries
1753 # if they are set up to select only a single field.
1754 raise TypeError("Cannot use multi-field values as a filter value.")
1755 query = self.query.resolve_expression(*args, **kwargs)
1756 query._db = self._db
1757 return query
1758
1759 resolve_expression.queryset_only = True
1760
1761 def _add_hints(self, **hints):
1762 """
1763 Update hinting information for use by routers. Add new key/values or
1764 overwrite existing key/values.
1765 """
1766 self._hints.update(hints)
1767
1768 def _has_filters(self):
1769 """
1770 Check if this QuerySet has any filtering going on. This isn't
1771 equivalent with checking if all objects are present in results, for
1772 example, qs[1:]._has_filters() -> False.
1773 """
1774 return self.query.has_filters()
1775
1776 @staticmethod
1777 def _validate_values_are_expressions(values, method_name):
1778 invalid_args = sorted(
1779 str(arg) for arg in values if not hasattr(arg, "resolve_expression")
1780 )
1781 if invalid_args:
1782 raise TypeError(
1783 "QuerySet.{}() received non-expression(s): {}.".format(
1784 method_name,
1785 ", ".join(invalid_args),
1786 )
1787 )
1788
1789 def _not_support_combined_queries(self, operation_name):
1790 if self.query.combinator:
1791 raise NotSupportedError(
1792 f"Calling QuerySet.{operation_name}() after {self.query.combinator}() is not supported."
1793 )
1794
1795 def _check_operator_queryset(self, other, operator_):
1796 if self.query.combinator or other.query.combinator:
1797 raise TypeError(f"Cannot use {operator_} operator with combined queryset.")
1798
1799 def _check_ordering_first_last_queryset_aggregation(self, method):
1800 if isinstance(self.query.group_by, tuple) and not any(
1801 col.output_field is self.model._meta.pk for col in self.query.group_by
1802 ):
1803 raise TypeError(
1804 f"Cannot use QuerySet.{method}() on an unordered queryset performing "
1805 f"aggregation. Add an ordering with order_by()."
1806 )
1807
1808
1809class InstanceCheckMeta(type):
1810 def __instancecheck__(self, instance):
1811 return isinstance(instance, QuerySet) and instance.query.is_empty()
1812
1813
1814class EmptyQuerySet(metaclass=InstanceCheckMeta):
1815 """
1816 Marker class to checking if a queryset is empty by .none():
1817 isinstance(qs.none(), EmptyQuerySet) -> True
1818 """
1819
1820 def __init__(self, *args, **kwargs):
1821 raise TypeError("EmptyQuerySet can't be instantiated")
1822
1823
1824class RawQuerySet:
1825 """
1826 Provide an iterator which converts the results of raw SQL queries into
1827 annotated model instances.
1828 """
1829
1830 def __init__(
1831 self,
1832 raw_query,
1833 model=None,
1834 query=None,
1835 params=(),
1836 translations=None,
1837 using=None,
1838 hints=None,
1839 ):
1840 self.raw_query = raw_query
1841 self.model = model
1842 self._db = using
1843 self._hints = hints or {}
1844 self.query = query or sql.RawQuery(sql=raw_query, using=self.db, params=params)
1845 self.params = params
1846 self.translations = translations or {}
1847 self._result_cache = None
1848 self._prefetch_related_lookups = ()
1849 self._prefetch_done = False
1850
1851 def resolve_model_init_order(self):
1852 """Resolve the init field names and value positions."""
1853 converter = connections[self.db].introspection.identifier_converter
1854 model_init_fields = [
1855 f for f in self.model._meta.fields if converter(f.column) in self.columns
1856 ]
1857 annotation_fields = [
1858 (column, pos)
1859 for pos, column in enumerate(self.columns)
1860 if column not in self.model_fields
1861 ]
1862 model_init_order = [
1863 self.columns.index(converter(f.column)) for f in model_init_fields
1864 ]
1865 model_init_names = [f.attname for f in model_init_fields]
1866 return model_init_names, model_init_order, annotation_fields
1867
1868 def prefetch_related(self, *lookups):
1869 """Same as QuerySet.prefetch_related()"""
1870 clone = self._clone()
1871 if lookups == (None,):
1872 clone._prefetch_related_lookups = ()
1873 else:
1874 clone._prefetch_related_lookups = clone._prefetch_related_lookups + lookups
1875 return clone
1876
1877 def _prefetch_related_objects(self):
1878 prefetch_related_objects(self._result_cache, *self._prefetch_related_lookups)
1879 self._prefetch_done = True
1880
1881 def _clone(self):
1882 """Same as QuerySet._clone()"""
1883 c = self.__class__(
1884 self.raw_query,
1885 model=self.model,
1886 query=self.query,
1887 params=self.params,
1888 translations=self.translations,
1889 using=self._db,
1890 hints=self._hints,
1891 )
1892 c._prefetch_related_lookups = self._prefetch_related_lookups[:]
1893 return c
1894
1895 def _fetch_all(self):
1896 if self._result_cache is None:
1897 self._result_cache = list(self.iterator())
1898 if self._prefetch_related_lookups and not self._prefetch_done:
1899 self._prefetch_related_objects()
1900
1901 def __len__(self):
1902 self._fetch_all()
1903 return len(self._result_cache)
1904
1905 def __bool__(self):
1906 self._fetch_all()
1907 return bool(self._result_cache)
1908
1909 def __iter__(self):
1910 self._fetch_all()
1911 return iter(self._result_cache)
1912
1913 def iterator(self):
1914 yield from RawModelIterable(self)
1915
1916 def __repr__(self):
1917 return f"<{self.__class__.__name__}: {self.query}>"
1918
1919 def __getitem__(self, k):
1920 return list(self)[k]
1921
1922 @property
1923 def db(self):
1924 """Return the database used if this query is executed now."""
1925 return self._db or router.db_for_read(self.model, **self._hints)
1926
1927 def using(self, alias):
1928 """Select the database this RawQuerySet should execute against."""
1929 return RawQuerySet(
1930 self.raw_query,
1931 model=self.model,
1932 query=self.query.chain(using=alias),
1933 params=self.params,
1934 translations=self.translations,
1935 using=alias,
1936 )
1937
1938 @cached_property
1939 def columns(self):
1940 """
1941 A list of model field names in the order they'll appear in the
1942 query results.
1943 """
1944 columns = self.query.get_columns()
1945 # Adjust any column names which don't match field names
1946 for query_name, model_name in self.translations.items():
1947 # Ignore translations for nonexistent column names
1948 try:
1949 index = columns.index(query_name)
1950 except ValueError:
1951 pass
1952 else:
1953 columns[index] = model_name
1954 return columns
1955
1956 @cached_property
1957 def model_fields(self):
1958 """A dict mapping column names to model field names."""
1959 converter = connections[self.db].introspection.identifier_converter
1960 model_fields = {}
1961 for field in self.model._meta.fields:
1962 name, column = field.get_attname_column()
1963 model_fields[converter(column)] = field
1964 return model_fields
1965
1966
1967class Prefetch:
1968 def __init__(self, lookup, queryset=None, to_attr=None):
1969 # `prefetch_through` is the path we traverse to perform the prefetch.
1970 self.prefetch_through = lookup
1971 # `prefetch_to` is the path to the attribute that stores the result.
1972 self.prefetch_to = lookup
1973 if queryset is not None and (
1974 isinstance(queryset, RawQuerySet)
1975 or (
1976 hasattr(queryset, "_iterable_class")
1977 and not issubclass(queryset._iterable_class, ModelIterable)
1978 )
1979 ):
1980 raise ValueError(
1981 "Prefetch querysets cannot use raw(), values(), and values_list()."
1982 )
1983 if to_attr:
1984 self.prefetch_to = LOOKUP_SEP.join(
1985 lookup.split(LOOKUP_SEP)[:-1] + [to_attr]
1986 )
1987
1988 self.queryset = queryset
1989 self.to_attr = to_attr
1990
1991 def __getstate__(self):
1992 obj_dict = self.__dict__.copy()
1993 if self.queryset is not None:
1994 queryset = self.queryset._chain()
1995 # Prevent the QuerySet from being evaluated
1996 queryset._result_cache = []
1997 queryset._prefetch_done = True
1998 obj_dict["queryset"] = queryset
1999 return obj_dict
2000
2001 def add_prefix(self, prefix):
2002 self.prefetch_through = prefix + LOOKUP_SEP + self.prefetch_through
2003 self.prefetch_to = prefix + LOOKUP_SEP + self.prefetch_to
2004
2005 def get_current_prefetch_to(self, level):
2006 return LOOKUP_SEP.join(self.prefetch_to.split(LOOKUP_SEP)[: level + 1])
2007
2008 def get_current_to_attr(self, level):
2009 parts = self.prefetch_to.split(LOOKUP_SEP)
2010 to_attr = parts[level]
2011 as_attr = self.to_attr and level == len(parts) - 1
2012 return to_attr, as_attr
2013
2014 def get_current_queryset(self, level):
2015 if self.get_current_prefetch_to(level) == self.prefetch_to:
2016 return self.queryset
2017 return None
2018
2019 def __eq__(self, other):
2020 if not isinstance(other, Prefetch):
2021 return NotImplemented
2022 return self.prefetch_to == other.prefetch_to
2023
2024 def __hash__(self):
2025 return hash((self.__class__, self.prefetch_to))
2026
2027
2028def normalize_prefetch_lookups(lookups, prefix=None):
2029 """Normalize lookups into Prefetch objects."""
2030 ret = []
2031 for lookup in lookups:
2032 if not isinstance(lookup, Prefetch):
2033 lookup = Prefetch(lookup)
2034 if prefix:
2035 lookup.add_prefix(prefix)
2036 ret.append(lookup)
2037 return ret
2038
2039
2040def prefetch_related_objects(model_instances, *related_lookups):
2041 """
2042 Populate prefetched object caches for a list of model instances based on
2043 the lookups/Prefetch instances given.
2044 """
2045 if not model_instances:
2046 return # nothing to do
2047
2048 # We need to be able to dynamically add to the list of prefetch_related
2049 # lookups that we look up (see below). So we need some book keeping to
2050 # ensure we don't do duplicate work.
2051 done_queries = {} # dictionary of things like 'foo__bar': [results]
2052
2053 auto_lookups = set() # we add to this as we go through.
2054 followed_descriptors = set() # recursion protection
2055
2056 all_lookups = normalize_prefetch_lookups(reversed(related_lookups))
2057 while all_lookups:
2058 lookup = all_lookups.pop()
2059 if lookup.prefetch_to in done_queries:
2060 if lookup.queryset is not None:
2061 raise ValueError(
2062 f"'{lookup.prefetch_to}' lookup was already seen with a different queryset. "
2063 "You may need to adjust the ordering of your lookups."
2064 )
2065
2066 continue
2067
2068 # Top level, the list of objects to decorate is the result cache
2069 # from the primary QuerySet. It won't be for deeper levels.
2070 obj_list = model_instances
2071
2072 through_attrs = lookup.prefetch_through.split(LOOKUP_SEP)
2073 for level, through_attr in enumerate(through_attrs):
2074 # Prepare main instances
2075 if not obj_list:
2076 break
2077
2078 prefetch_to = lookup.get_current_prefetch_to(level)
2079 if prefetch_to in done_queries:
2080 # Skip any prefetching, and any object preparation
2081 obj_list = done_queries[prefetch_to]
2082 continue
2083
2084 # Prepare objects:
2085 good_objects = True
2086 for obj in obj_list:
2087 # Since prefetching can re-use instances, it is possible to have
2088 # the same instance multiple times in obj_list, so obj might
2089 # already be prepared.
2090 if not hasattr(obj, "_prefetched_objects_cache"):
2091 try:
2092 obj._prefetched_objects_cache = {}
2093 except (AttributeError, TypeError):
2094 # Must be an immutable object from
2095 # values_list(flat=True), for example (TypeError) or
2096 # a QuerySet subclass that isn't returning Model
2097 # instances (AttributeError), either in Plain or a 3rd
2098 # party. prefetch_related() doesn't make sense, so quit.
2099 good_objects = False
2100 break
2101 if not good_objects:
2102 break
2103
2104 # Descend down tree
2105
2106 # We assume that objects retrieved are homogeneous (which is the premise
2107 # of prefetch_related), so what applies to first object applies to all.
2108 first_obj = obj_list[0]
2109 to_attr = lookup.get_current_to_attr(level)[0]
2110 prefetcher, descriptor, attr_found, is_fetched = get_prefetcher(
2111 first_obj, through_attr, to_attr
2112 )
2113
2114 if not attr_found:
2115 raise AttributeError(
2116 f"Cannot find '{through_attr}' on {first_obj.__class__.__name__} object, '{lookup.prefetch_through}' is an invalid "
2117 "parameter to prefetch_related()"
2118 )
2119
2120 if level == len(through_attrs) - 1 and prefetcher is None:
2121 # Last one, this *must* resolve to something that supports
2122 # prefetching, otherwise there is no point adding it and the
2123 # developer asking for it has made a mistake.
2124 raise ValueError(
2125 f"'{lookup.prefetch_through}' does not resolve to an item that supports "
2126 "prefetching - this is an invalid parameter to "
2127 "prefetch_related()."
2128 )
2129
2130 obj_to_fetch = None
2131 if prefetcher is not None:
2132 obj_to_fetch = [obj for obj in obj_list if not is_fetched(obj)]
2133
2134 if obj_to_fetch:
2135 obj_list, additional_lookups = prefetch_one_level(
2136 obj_to_fetch,
2137 prefetcher,
2138 lookup,
2139 level,
2140 )
2141 # We need to ensure we don't keep adding lookups from the
2142 # same relationships to stop infinite recursion. So, if we
2143 # are already on an automatically added lookup, don't add
2144 # the new lookups from relationships we've seen already.
2145 if not (
2146 prefetch_to in done_queries
2147 and lookup in auto_lookups
2148 and descriptor in followed_descriptors
2149 ):
2150 done_queries[prefetch_to] = obj_list
2151 new_lookups = normalize_prefetch_lookups(
2152 reversed(additional_lookups), prefetch_to
2153 )
2154 auto_lookups.update(new_lookups)
2155 all_lookups.extend(new_lookups)
2156 followed_descriptors.add(descriptor)
2157 else:
2158 # Either a singly related object that has already been fetched
2159 # (e.g. via select_related), or hopefully some other property
2160 # that doesn't support prefetching but needs to be traversed.
2161
2162 # We replace the current list of parent objects with the list
2163 # of related objects, filtering out empty or missing values so
2164 # that we can continue with nullable or reverse relations.
2165 new_obj_list = []
2166 for obj in obj_list:
2167 if through_attr in getattr(obj, "_prefetched_objects_cache", ()):
2168 # If related objects have been prefetched, use the
2169 # cache rather than the object's through_attr.
2170 new_obj = list(obj._prefetched_objects_cache.get(through_attr))
2171 else:
2172 try:
2173 new_obj = getattr(obj, through_attr)
2174 except exceptions.ObjectDoesNotExist:
2175 continue
2176 if new_obj is None:
2177 continue
2178 # We special-case `list` rather than something more generic
2179 # like `Iterable` because we don't want to accidentally match
2180 # user models that define __iter__.
2181 if isinstance(new_obj, list):
2182 new_obj_list.extend(new_obj)
2183 else:
2184 new_obj_list.append(new_obj)
2185 obj_list = new_obj_list
2186
2187
2188def get_prefetcher(instance, through_attr, to_attr):
2189 """
2190 For the attribute 'through_attr' on the given instance, find
2191 an object that has a get_prefetch_queryset().
2192 Return a 4 tuple containing:
2193 (the object with get_prefetch_queryset (or None),
2194 the descriptor object representing this relationship (or None),
2195 a boolean that is False if the attribute was not found at all,
2196 a function that takes an instance and returns a boolean that is True if
2197 the attribute has already been fetched for that instance)
2198 """
2199
2200 def has_to_attr_attribute(instance):
2201 return hasattr(instance, to_attr)
2202
2203 prefetcher = None
2204 is_fetched = has_to_attr_attribute
2205
2206 # For singly related objects, we have to avoid getting the attribute
2207 # from the object, as this will trigger the query. So we first try
2208 # on the class, in order to get the descriptor object.
2209 rel_obj_descriptor = getattr(instance.__class__, through_attr, None)
2210 if rel_obj_descriptor is None:
2211 attr_found = hasattr(instance, through_attr)
2212 else:
2213 attr_found = True
2214 if rel_obj_descriptor:
2215 # singly related object, descriptor object has the
2216 # get_prefetch_queryset() method.
2217 if hasattr(rel_obj_descriptor, "get_prefetch_queryset"):
2218 prefetcher = rel_obj_descriptor
2219 is_fetched = rel_obj_descriptor.is_cached
2220 else:
2221 # descriptor doesn't support prefetching, so we go ahead and get
2222 # the attribute on the instance rather than the class to
2223 # support many related managers
2224 rel_obj = getattr(instance, through_attr)
2225 if hasattr(rel_obj, "get_prefetch_queryset"):
2226 prefetcher = rel_obj
2227 if through_attr != to_attr:
2228 # Special case cached_property instances because hasattr
2229 # triggers attribute computation and assignment.
2230 if isinstance(
2231 getattr(instance.__class__, to_attr, None), cached_property
2232 ):
2233
2234 def has_cached_property(instance):
2235 return to_attr in instance.__dict__
2236
2237 is_fetched = has_cached_property
2238 else:
2239
2240 def in_prefetched_cache(instance):
2241 return through_attr in instance._prefetched_objects_cache
2242
2243 is_fetched = in_prefetched_cache
2244 return prefetcher, rel_obj_descriptor, attr_found, is_fetched
2245
2246
2247def prefetch_one_level(instances, prefetcher, lookup, level):
2248 """
2249 Helper function for prefetch_related_objects().
2250
2251 Run prefetches on all instances using the prefetcher object,
2252 assigning results to relevant caches in instance.
2253
2254 Return the prefetched objects along with any additional prefetches that
2255 must be done due to prefetch_related lookups found from default managers.
2256 """
2257 # prefetcher must have a method get_prefetch_queryset() which takes a list
2258 # of instances, and returns a tuple:
2259
2260 # (queryset of instances of self.model that are related to passed in instances,
2261 # callable that gets value to be matched for returned instances,
2262 # callable that gets value to be matched for passed in instances,
2263 # boolean that is True for singly related objects,
2264 # cache or field name to assign to,
2265 # boolean that is True when the previous argument is a cache name vs a field name).
2266
2267 # The 'values to be matched' must be hashable as they will be used
2268 # in a dictionary.
2269
2270 (
2271 rel_qs,
2272 rel_obj_attr,
2273 instance_attr,
2274 single,
2275 cache_name,
2276 is_descriptor,
2277 ) = prefetcher.get_prefetch_queryset(instances, lookup.get_current_queryset(level))
2278 # We have to handle the possibility that the QuerySet we just got back
2279 # contains some prefetch_related lookups. We don't want to trigger the
2280 # prefetch_related functionality by evaluating the query. Rather, we need
2281 # to merge in the prefetch_related lookups.
2282 # Copy the lookups in case it is a Prefetch object which could be reused
2283 # later (happens in nested prefetch_related).
2284 additional_lookups = [
2285 copy.copy(additional_lookup)
2286 for additional_lookup in getattr(rel_qs, "_prefetch_related_lookups", ())
2287 ]
2288 if additional_lookups:
2289 # Don't need to clone because the manager should have given us a fresh
2290 # instance, so we access an internal instead of using public interface
2291 # for performance reasons.
2292 rel_qs._prefetch_related_lookups = ()
2293
2294 all_related_objects = list(rel_qs)
2295
2296 rel_obj_cache = {}
2297 for rel_obj in all_related_objects:
2298 rel_attr_val = rel_obj_attr(rel_obj)
2299 rel_obj_cache.setdefault(rel_attr_val, []).append(rel_obj)
2300
2301 to_attr, as_attr = lookup.get_current_to_attr(level)
2302 # Make sure `to_attr` does not conflict with a field.
2303 if as_attr and instances:
2304 # We assume that objects retrieved are homogeneous (which is the premise
2305 # of prefetch_related), so what applies to first object applies to all.
2306 model = instances[0].__class__
2307 try:
2308 model._meta.get_field(to_attr)
2309 except exceptions.FieldDoesNotExist:
2310 pass
2311 else:
2312 msg = "to_attr={} conflicts with a field on the {} model."
2313 raise ValueError(msg.format(to_attr, model.__name__))
2314
2315 # Whether or not we're prefetching the last part of the lookup.
2316 leaf = len(lookup.prefetch_through.split(LOOKUP_SEP)) - 1 == level
2317
2318 for obj in instances:
2319 instance_attr_val = instance_attr(obj)
2320 vals = rel_obj_cache.get(instance_attr_val, [])
2321
2322 if single:
2323 val = vals[0] if vals else None
2324 if as_attr:
2325 # A to_attr has been given for the prefetch.
2326 setattr(obj, to_attr, val)
2327 elif is_descriptor:
2328 # cache_name points to a field name in obj.
2329 # This field is a descriptor for a related object.
2330 setattr(obj, cache_name, val)
2331 else:
2332 # No to_attr has been given for this prefetch operation and the
2333 # cache_name does not point to a descriptor. Store the value of
2334 # the field in the object's field cache.
2335 obj._state.fields_cache[cache_name] = val
2336 else:
2337 if as_attr:
2338 setattr(obj, to_attr, vals)
2339 else:
2340 manager = getattr(obj, to_attr)
2341 if leaf and lookup.queryset is not None:
2342 qs = manager._apply_rel_filters(lookup.queryset)
2343 else:
2344 qs = manager.get_queryset()
2345 qs._result_cache = vals
2346 # We don't want the individual qs doing prefetch_related now,
2347 # since we have merged this into the current work.
2348 qs._prefetch_done = True
2349 obj._prefetched_objects_cache[cache_name] = qs
2350 return all_related_objects, additional_lookups
2351
2352
2353class RelatedPopulator:
2354 """
2355 RelatedPopulator is used for select_related() object instantiation.
2356
2357 The idea is that each select_related() model will be populated by a
2358 different RelatedPopulator instance. The RelatedPopulator instances get
2359 klass_info and select (computed in SQLCompiler) plus the used db as
2360 input for initialization. That data is used to compute which columns
2361 to use, how to instantiate the model, and how to populate the links
2362 between the objects.
2363
2364 The actual creation of the objects is done in populate() method. This
2365 method gets row and from_obj as input and populates the select_related()
2366 model instance.
2367 """
2368
2369 def __init__(self, klass_info, select, db):
2370 self.db = db
2371 # Pre-compute needed attributes. The attributes are:
2372 # - model_cls: the possibly deferred model class to instantiate
2373 # - either:
2374 # - cols_start, cols_end: usually the columns in the row are
2375 # in the same order model_cls.__init__ expects them, so we
2376 # can instantiate by model_cls(*row[cols_start:cols_end])
2377 # - reorder_for_init: When select_related descends to a child
2378 # class, then we want to reuse the already selected parent
2379 # data. However, in this case the parent data isn't necessarily
2380 # in the same order that Model.__init__ expects it to be, so
2381 # we have to reorder the parent data. The reorder_for_init
2382 # attribute contains a function used to reorder the field data
2383 # in the order __init__ expects it.
2384 # - pk_idx: the index of the primary key field in the reordered
2385 # model data. Used to check if a related object exists at all.
2386 # - init_list: the field attnames fetched from the database. For
2387 # deferred models this isn't the same as all attnames of the
2388 # model's fields.
2389 # - related_populators: a list of RelatedPopulator instances if
2390 # select_related() descends to related models from this model.
2391 # - local_setter, remote_setter: Methods to set cached values on
2392 # the object being populated and on the remote object. Usually
2393 # these are Field.set_cached_value() methods.
2394 select_fields = klass_info["select_fields"]
2395
2396 self.cols_start = select_fields[0]
2397 self.cols_end = select_fields[-1] + 1
2398 self.init_list = [
2399 f[0].target.attname for f in select[self.cols_start : self.cols_end]
2400 ]
2401 self.reorder_for_init = None
2402
2403 self.model_cls = klass_info["model"]
2404 self.pk_idx = self.init_list.index(self.model_cls._meta.pk.attname)
2405 self.related_populators = get_related_populators(klass_info, select, self.db)
2406 self.local_setter = klass_info["local_setter"]
2407 self.remote_setter = klass_info["remote_setter"]
2408
2409 def populate(self, row, from_obj):
2410 if self.reorder_for_init:
2411 obj_data = self.reorder_for_init(row)
2412 else:
2413 obj_data = row[self.cols_start : self.cols_end]
2414 if obj_data[self.pk_idx] is None:
2415 obj = None
2416 else:
2417 obj = self.model_cls.from_db(self.db, self.init_list, obj_data)
2418 for rel_iter in self.related_populators:
2419 rel_iter.populate(row, obj)
2420 self.local_setter(from_obj, obj)
2421 if obj is not None:
2422 self.remote_setter(obj, from_obj)
2423
2424
2425def get_related_populators(klass_info, select, db):
2426 iterators = []
2427 related_klass_infos = klass_info.get("related_klass_infos", [])
2428 for rel_klass_info in related_klass_infos:
2429 rel_cls = RelatedPopulator(rel_klass_info, select, db)
2430 iterators.append(rel_cls)
2431 return iterators