summaryrefslogtreecommitdiff
path: root/lib/python2.7/site-packages/django/db/models/query.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python2.7/site-packages/django/db/models/query.py')
-rw-r--r--lib/python2.7/site-packages/django/db/models/query.py1731
1 files changed, 0 insertions, 1731 deletions
diff --git a/lib/python2.7/site-packages/django/db/models/query.py b/lib/python2.7/site-packages/django/db/models/query.py
deleted file mode 100644
index 44047d4..0000000
--- a/lib/python2.7/site-packages/django/db/models/query.py
+++ /dev/null
@@ -1,1731 +0,0 @@
-"""
-The main QuerySet implementation. This provides the public API for the ORM.
-"""
-
-import copy
-import itertools
-import sys
-import warnings
-
-from django.conf import settings
-from django.core import exceptions
-from django.db import connections, router, transaction, DatabaseError
-from django.db.models.constants import LOOKUP_SEP
-from django.db.models.fields import AutoField
-from django.db.models.query_utils import (Q, select_related_descend,
- deferred_class_factory, InvalidQuery)
-from django.db.models.deletion import Collector
-from django.db.models import sql
-from django.utils.functional import partition
-from django.utils import six
-from django.utils import timezone
-
-# The maximum number of items to display in a QuerySet.__repr__
-REPR_OUTPUT_SIZE = 20
-
-# Pull into this namespace for backwards compatibility.
-EmptyResultSet = sql.EmptyResultSet
-
-
-class QuerySet(object):
- """
- Represents a lazy database lookup for a set of objects.
- """
- def __init__(self, model=None, query=None, using=None):
- self.model = model
- self._db = using
- self.query = query or sql.Query(self.model)
- self._result_cache = None
- self._sticky_filter = False
- self._for_write = False
- self._prefetch_related_lookups = []
- self._prefetch_done = False
- self._known_related_objects = {} # {rel_field, {pk: rel_obj}}
-
- ########################
- # PYTHON MAGIC METHODS #
- ########################
-
- def __deepcopy__(self, memo):
- """
- Deep copy of a QuerySet doesn't populate the cache
- """
- obj = self.__class__()
- for k, v in self.__dict__.items():
- if k == '_result_cache':
- obj.__dict__[k] = None
- else:
- obj.__dict__[k] = copy.deepcopy(v, memo)
- return obj
-
- def __getstate__(self):
- """
- Allows the QuerySet to be pickled.
- """
- # Force the cache to be fully populated.
- self._fetch_all()
- obj_dict = self.__dict__.copy()
- return obj_dict
-
- def __repr__(self):
- data = list(self[:REPR_OUTPUT_SIZE + 1])
- if len(data) > REPR_OUTPUT_SIZE:
- data[-1] = "...(remaining elements truncated)..."
- return repr(data)
-
- def __len__(self):
- self._fetch_all()
- return len(self._result_cache)
-
- def __iter__(self):
- """
- The queryset iterator protocol uses three nested iterators in the
- default case:
- 1. sql.compiler:execute_sql()
- - Returns 100 rows at time (constants.GET_ITERATOR_CHUNK_SIZE)
- using cursor.fetchmany(). This part is responsible for
- doing some column masking, and returning the rows in chunks.
- 2. sql/compiler.results_iter()
- - Returns one row at time. At this point the rows are still just
- tuples. In some cases the return values are converted to
- Python values at this location (see resolve_columns(),
- resolve_aggregate()).
- 3. self.iterator()
- - Responsible for turning the rows into model objects.
- """
- self._fetch_all()
- return iter(self._result_cache)
-
- def __nonzero__(self):
- self._fetch_all()
- return bool(self._result_cache)
-
- def __getitem__(self, k):
- """
- Retrieves an item or slice from the set of results.
- """
- if not isinstance(k, (slice,) + six.integer_types):
- raise TypeError
- assert ((not isinstance(k, slice) and (k >= 0))
- or (isinstance(k, slice) and (k.start is None or k.start >= 0)
- and (k.stop is None or k.stop >= 0))), \
- "Negative indexing is not supported."
-
- if self._result_cache is not None:
- return self._result_cache[k]
-
- if isinstance(k, slice):
- qs = self._clone()
- if k.start is not None:
- start = int(k.start)
- else:
- start = None
- if k.stop is not None:
- stop = int(k.stop)
- else:
- stop = None
- qs.query.set_limits(start, stop)
- return list(qs)[::k.step] if k.step else qs
-
- qs = self._clone()
- qs.query.set_limits(k, k + 1)
- return list(qs)[0]
-
- def __and__(self, other):
- self._merge_sanity_check(other)
- if isinstance(other, EmptyQuerySet):
- return other
- if isinstance(self, EmptyQuerySet):
- return self
- combined = self._clone()
- combined._merge_known_related_objects(other)
- combined.query.combine(other.query, sql.AND)
- return combined
-
- def __or__(self, other):
- self._merge_sanity_check(other)
- if isinstance(self, EmptyQuerySet):
- return other
- if isinstance(other, EmptyQuerySet):
- return self
- combined = self._clone()
- combined._merge_known_related_objects(other)
- combined.query.combine(other.query, sql.OR)
- return combined
-
- ####################################
- # METHODS THAT DO DATABASE QUERIES #
- ####################################
-
- def iterator(self):
- """
- An iterator over the results from applying this QuerySet to the
- database.
- """
- fill_cache = False
- if connections[self.db].features.supports_select_related:
- fill_cache = self.query.select_related
- if isinstance(fill_cache, dict):
- requested = fill_cache
- else:
- requested = None
- max_depth = self.query.max_depth
-
- extra_select = list(self.query.extra_select)
- aggregate_select = list(self.query.aggregate_select)
-
- only_load = self.query.get_loaded_field_names()
- if not fill_cache:
- fields = self.model._meta.concrete_fields
-
- load_fields = []
- # If only/defer clauses have been specified,
- # build the list of fields that are to be loaded.
- if only_load:
- for field, model in self.model._meta.get_concrete_fields_with_model():
- if model is None:
- model = self.model
- try:
- if field.name in only_load[model]:
- # Add a field that has been explicitly included
- load_fields.append(field.name)
- except KeyError:
- # Model wasn't explicitly listed in the only_load table
- # Therefore, we need to load all fields from this model
- load_fields.append(field.name)
-
- index_start = len(extra_select)
- aggregate_start = index_start + len(load_fields or self.model._meta.concrete_fields)
-
- skip = None
- if load_fields and not fill_cache:
- # Some fields have been deferred, so we have to initialise
- # via keyword arguments.
- skip = set()
- init_list = []
- for field in fields:
- if field.name not in load_fields:
- skip.add(field.attname)
- else:
- init_list.append(field.attname)
- model_cls = deferred_class_factory(self.model, skip)
-
- # Cache db and model outside the loop
- db = self.db
- model = self.model
- compiler = self.query.get_compiler(using=db)
- if fill_cache:
- klass_info = get_klass_info(model, max_depth=max_depth,
- requested=requested, only_load=only_load)
- for row in compiler.results_iter():
- if fill_cache:
- obj, _ = get_cached_row(row, index_start, db, klass_info,
- offset=len(aggregate_select))
- else:
- # Omit aggregates in object creation.
- row_data = row[index_start:aggregate_start]
- if skip:
- obj = model_cls(**dict(zip(init_list, row_data)))
- else:
- obj = model(*row_data)
-
- # Store the source database of the object
- obj._state.db = db
- # This object came from the database; it's not being added.
- obj._state.adding = False
-
- if extra_select:
- for i, k in enumerate(extra_select):
- setattr(obj, k, row[i])
-
- # Add the aggregates to the model
- if aggregate_select:
- for i, aggregate in enumerate(aggregate_select):
- setattr(obj, aggregate, row[i + aggregate_start])
-
- # Add the known related objects to the model, if there are any
- if self._known_related_objects:
- for field, rel_objs in self._known_related_objects.items():
- pk = getattr(obj, field.get_attname())
- try:
- rel_obj = rel_objs[pk]
- except KeyError:
- pass # may happen in qs1 | qs2 scenarios
- else:
- setattr(obj, field.name, rel_obj)
-
- yield obj
-
- def aggregate(self, *args, **kwargs):
- """
- Returns a dictionary containing the calculations (aggregation)
- over the current queryset
-
- If args is present the expression is passed as a kwarg using
- the Aggregate object's default alias.
- """
- if self.query.distinct_fields:
- raise NotImplementedError("aggregate() + distinct(fields) not implemented.")
- for arg in args:
- kwargs[arg.default_alias] = arg
-
- query = self.query.clone()
-
- for (alias, aggregate_expr) in kwargs.items():
- query.add_aggregate(aggregate_expr, self.model, alias,
- is_summary=True)
-
- return query.get_aggregation(using=self.db)
-
- def count(self):
- """
- Performs a SELECT COUNT() and returns the number of records as an
- integer.
-
- If the QuerySet is already fully cached this simply returns the length
- of the cached results set to avoid multiple SELECT COUNT(*) calls.
- """
- if self._result_cache is not None:
- return len(self._result_cache)
-
- return self.query.get_count(using=self.db)
-
- def get(self, *args, **kwargs):
- """
- Performs the query and returns a single object matching the given
- keyword arguments.
- """
- clone = self.filter(*args, **kwargs)
- if self.query.can_filter():
- clone = clone.order_by()
- num = len(clone)
- if num == 1:
- return clone._result_cache[0]
- if not num:
- raise self.model.DoesNotExist(
- "%s matching query does not exist." %
- self.model._meta.object_name)
- raise self.model.MultipleObjectsReturned(
- "get() returned more than one %s -- it returned %s!" %
- (self.model._meta.object_name, num))
-
- def create(self, **kwargs):
- """
- Creates a new object with the given kwargs, saving it to the database
- and returning the created object.
- """
- obj = self.model(**kwargs)
- self._for_write = True
- obj.save(force_insert=True, using=self.db)
- return obj
-
- def bulk_create(self, objs, batch_size=None):
- """
- Inserts each of the instances into the database. This does *not* call
- save() on each of the instances, does not send any pre/post save
- signals, and does not set the primary key attribute if it is an
- autoincrement field.
- """
- # So this case is fun. When you bulk insert you don't get the primary
- # keys back (if it's an autoincrement), so you can't insert into the
- # child tables which references this. There are two workarounds, 1)
- # this could be implemented if you didn't have an autoincrement pk,
- # and 2) you could do it by doing O(n) normal inserts into the parent
- # tables to get the primary keys back, and then doing a single bulk
- # insert into the childmost table. Some databases might allow doing
- # this by using RETURNING clause for the insert query. We're punting
- # on these for now because they are relatively rare cases.
- assert batch_size is None or batch_size > 0
- if self.model._meta.parents:
- raise ValueError("Can't bulk create an inherited model")
- if not objs:
- return objs
- self._for_write = True
- connection = connections[self.db]
- fields = self.model._meta.local_concrete_fields
- with transaction.commit_on_success_unless_managed(using=self.db):
- if (connection.features.can_combine_inserts_with_and_without_auto_increment_pk
- and self.model._meta.has_auto_field):
- self._batched_insert(objs, fields, batch_size)
- else:
- objs_with_pk, objs_without_pk = partition(lambda o: o.pk is None, objs)
- if objs_with_pk:
- self._batched_insert(objs_with_pk, fields, batch_size)
- if objs_without_pk:
- fields= [f for f in fields if not isinstance(f, AutoField)]
- self._batched_insert(objs_without_pk, fields, batch_size)
-
- return objs
-
- def get_or_create(self, **kwargs):
- """
- Looks up an object with the given kwargs, creating one if necessary.
- Returns a tuple of (object, created), where created is a boolean
- specifying whether an object was created.
- """
- defaults = kwargs.pop('defaults', {})
- lookup = kwargs.copy()
- for f in self.model._meta.fields:
- if f.attname in lookup:
- lookup[f.name] = lookup.pop(f.attname)
- try:
- self._for_write = True
- return self.get(**lookup), False
- except self.model.DoesNotExist:
- try:
- params = dict((k, v) for k, v in kwargs.items() if LOOKUP_SEP not in k)
- params.update(defaults)
- obj = self.model(**params)
- with transaction.atomic(using=self.db):
- obj.save(force_insert=True, using=self.db)
- return obj, True
- except DatabaseError:
- exc_info = sys.exc_info()
- try:
- return self.get(**lookup), False
- except self.model.DoesNotExist:
- # Re-raise the DatabaseError with its original traceback.
- six.reraise(*exc_info)
-
- def _earliest_or_latest(self, field_name=None, direction="-"):
- """
- Returns the latest object, according to the model's
- 'get_latest_by' option or optional given field_name.
- """
- order_by = field_name or getattr(self.model._meta, 'get_latest_by')
- assert bool(order_by), "earliest() and latest() require either a "\
- "field_name parameter or 'get_latest_by' in the model"
- assert self.query.can_filter(), \
- "Cannot change a query once a slice has been taken."
- obj = self._clone()
- obj.query.set_limits(high=1)
- obj.query.clear_ordering(force_empty=True)
- obj.query.add_ordering('%s%s' % (direction, order_by))
- return obj.get()
-
- def earliest(self, field_name=None):
- return self._earliest_or_latest(field_name=field_name, direction="")
-
- def latest(self, field_name=None):
- return self._earliest_or_latest(field_name=field_name, direction="-")
-
- def first(self):
- """
- Returns the first object of a query, returns None if no match is found.
- """
- qs = self if self.ordered else self.order_by('pk')
- try:
- return qs[0]
- except IndexError:
- return None
-
- def last(self):
- """
- Returns the last object of a query, returns None if no match is found.
- """
- qs = self.reverse() if self.ordered else self.order_by('-pk')
- try:
- return qs[0]
- except IndexError:
- return None
-
- def in_bulk(self, id_list):
- """
- Returns a dictionary mapping each of the given IDs to the object with
- that ID.
- """
- assert self.query.can_filter(), \
- "Cannot use 'limit' or 'offset' with in_bulk"
- if not id_list:
- return {}
- qs = self.filter(pk__in=id_list).order_by()
- return dict([(obj._get_pk_val(), obj) for obj in qs])
-
- def delete(self):
- """
- Deletes the records in the current QuerySet.
- """
- assert self.query.can_filter(), \
- "Cannot use 'limit' or 'offset' with delete."
-
- del_query = self._clone()
-
- # The delete is actually 2 queries - one to find related objects,
- # and one to delete. Make sure that the discovery of related
- # objects is performed on the same database as the deletion.
- del_query._for_write = True
-
- # Disable non-supported fields.
- del_query.query.select_for_update = False
- del_query.query.select_related = False
- del_query.query.clear_ordering(force_empty=True)
-
- collector = Collector(using=del_query.db)
- collector.collect(del_query)
- collector.delete()
-
- # Clear the result cache, in case this QuerySet gets reused.
- self._result_cache = None
- delete.alters_data = True
-
- def _raw_delete(self, using):
- """
- Deletes objects found from the given queryset in single direct SQL
- query. No signals are sent, and there is no protection for cascades.
- """
- sql.DeleteQuery(self.model).delete_qs(self, using)
- _raw_delete.alters_data = True
-
- def update(self, **kwargs):
- """
- Updates all elements in the current QuerySet, setting all the given
- fields to the appropriate values.
- """
- assert self.query.can_filter(), \
- "Cannot update a query once a slice has been taken."
- self._for_write = True
- query = self.query.clone(sql.UpdateQuery)
- query.add_update_values(kwargs)
- with transaction.commit_on_success_unless_managed(using=self.db):
- rows = query.get_compiler(self.db).execute_sql(None)
- self._result_cache = None
- return rows
- update.alters_data = True
-
- def _update(self, values):
- """
- A version of update that accepts field objects instead of field names.
- Used primarily for model saving and not intended for use by general
- code (it requires too much poking around at model internals to be
- useful at that level).
- """
- assert self.query.can_filter(), \
- "Cannot update a query once a slice has been taken."
- query = self.query.clone(sql.UpdateQuery)
- query.add_update_fields(values)
- self._result_cache = None
- return query.get_compiler(self.db).execute_sql(None)
- _update.alters_data = True
-
- def exists(self):
- if self._result_cache is None:
- return self.query.has_results(using=self.db)
- return bool(self._result_cache)
-
- def _prefetch_related_objects(self):
- # This method can only be called once the result cache has been filled.
- prefetch_related_objects(self._result_cache, self._prefetch_related_lookups)
- self._prefetch_done = True
-
- ##################################################
- # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
- ##################################################
-
- def values(self, *fields):
- return self._clone(klass=ValuesQuerySet, setup=True, _fields=fields)
-
- def values_list(self, *fields, **kwargs):
- flat = kwargs.pop('flat', False)
- if kwargs:
- raise TypeError('Unexpected keyword arguments to values_list: %s'
- % (list(kwargs),))
- if flat and len(fields) > 1:
- raise TypeError("'flat' is not valid when values_list is called with more than one field.")
- return self._clone(klass=ValuesListQuerySet, setup=True, flat=flat,
- _fields=fields)
-
- def dates(self, field_name, kind, order='ASC'):
- """
- Returns a list of date objects representing all available dates for
- the given field_name, scoped to 'kind'.
- """
- assert kind in ("year", "month", "day"), \
- "'kind' must be one of 'year', 'month' or 'day'."
- assert order in ('ASC', 'DESC'), \
- "'order' must be either 'ASC' or 'DESC'."
- return self._clone(klass=DateQuerySet, setup=True,
- _field_name=field_name, _kind=kind, _order=order)
-
- def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
- """
- Returns a list of datetime objects representing all available
- datetimes for the given field_name, scoped to 'kind'.
- """
- assert kind in ("year", "month", "day", "hour", "minute", "second"), \
- "'kind' must be one of 'year', 'month', 'day', 'hour', 'minute' or 'second'."
- assert order in ('ASC', 'DESC'), \
- "'order' must be either 'ASC' or 'DESC'."
- if settings.USE_TZ:
- if tzinfo is None:
- tzinfo = timezone.get_current_timezone()
- else:
- tzinfo = None
- return self._clone(klass=DateTimeQuerySet, setup=True,
- _field_name=field_name, _kind=kind, _order=order, _tzinfo=tzinfo)
-
- def none(self):
- """
- Returns an empty QuerySet.
- """
- clone = self._clone()
- clone.query.set_empty()
- return clone
-
- ##################################################################
- # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
- ##################################################################
-
- def all(self):
- """
- Returns a new QuerySet that is a copy of the current one. This allows a
- QuerySet to proxy for a model manager in some cases.
- """
- return self._clone()
-
- def filter(self, *args, **kwargs):
- """
- Returns a new QuerySet instance with the args ANDed to the existing
- set.
- """
- return self._filter_or_exclude(False, *args, **kwargs)
-
- def exclude(self, *args, **kwargs):
- """
- Returns a new QuerySet instance with NOT (args) ANDed to the existing
- set.
- """
- return self._filter_or_exclude(True, *args, **kwargs)
-
- def _filter_or_exclude(self, negate, *args, **kwargs):
- if args or kwargs:
- assert self.query.can_filter(), \
- "Cannot filter a query once a slice has been taken."
-
- clone = self._clone()
- if negate:
- clone.query.add_q(~Q(*args, **kwargs))
- else:
- clone.query.add_q(Q(*args, **kwargs))
- return clone
-
- def complex_filter(self, filter_obj):
- """
- Returns a new QuerySet instance with filter_obj added to the filters.
-
- filter_obj can be a Q object (or anything with an add_to_query()
- method) or a dictionary of keyword lookup arguments.
-
- This exists to support framework features such as 'limit_choices_to',
- and usually it will be more natural to use other methods.
- """
- if isinstance(filter_obj, Q) or hasattr(filter_obj, 'add_to_query'):
- clone = self._clone()
- clone.query.add_q(filter_obj)
- return clone
- else:
- return self._filter_or_exclude(None, **filter_obj)
-
- def select_for_update(self, **kwargs):
- """
- Returns a new QuerySet instance that will select objects with a
- FOR UPDATE lock.
- """
- # Default to false for nowait
- nowait = kwargs.pop('nowait', False)
- obj = self._clone()
- obj._for_write = True
- obj.query.select_for_update = True
- obj.query.select_for_update_nowait = nowait
- return obj
-
- def select_related(self, *fields, **kwargs):
- """
- Returns a new QuerySet instance that will select related objects.
-
- If fields are specified, they must be ForeignKey fields and only those
- related objects are included in the selection.
-
- If select_related(None) is called, the list is cleared.
- """
- if 'depth' in kwargs:
- warnings.warn('The "depth" keyword argument has been deprecated.\n'
- 'Use related field names instead.', DeprecationWarning, stacklevel=2)
- depth = kwargs.pop('depth', 0)
- if kwargs:
- raise TypeError('Unexpected keyword arguments to select_related: %s'
- % (list(kwargs),))
- obj = self._clone()
- if fields == (None,):
- obj.query.select_related = False
- elif fields:
- if depth:
- raise TypeError('Cannot pass both "depth" and fields to select_related()')
- obj.query.add_select_related(fields)
- else:
- obj.query.select_related = True
- if depth:
- obj.query.max_depth = depth
- return obj
-
- def prefetch_related(self, *lookups):
- """
- Returns a new QuerySet instance that will prefetch the specified
- Many-To-One and Many-To-Many related objects when the QuerySet is
- evaluated.
-
- When prefetch_related() is called more than once, the list of lookups to
- prefetch is appended to. If prefetch_related(None) is called, the
- the list is cleared.
- """
- clone = self._clone()
- if lookups == (None,):
- clone._prefetch_related_lookups = []
- else:
- clone._prefetch_related_lookups.extend(lookups)
- return clone
-
- def annotate(self, *args, **kwargs):
- """
- Return a query set in which the returned objects have been annotated
- with data aggregated from related fields.
- """
- for arg in args:
- if arg.default_alias in kwargs:
- raise ValueError("The named annotation '%s' conflicts with the "
- "default name for another annotation."
- % arg.default_alias)
- kwargs[arg.default_alias] = arg
-
- names = getattr(self, '_fields', None)
- if names is None:
- names = set(self.model._meta.get_all_field_names())
- for aggregate in kwargs:
- if aggregate in names:
- raise ValueError("The annotation '%s' conflicts with a field on "
- "the model." % aggregate)
-
- obj = self._clone()
-
- obj._setup_aggregate_query(list(kwargs))
-
- # Add the aggregates to the query
- for (alias, aggregate_expr) in kwargs.items():
- obj.query.add_aggregate(aggregate_expr, self.model, alias,
- is_summary=False)
-
- return obj
-
- def order_by(self, *field_names):
- """
- Returns a new QuerySet instance with the ordering changed.
- """
- assert self.query.can_filter(), \
- "Cannot reorder a query once a slice has been taken."
- obj = self._clone()
- obj.query.clear_ordering(force_empty=False)
- obj.query.add_ordering(*field_names)
- return obj
-
- def distinct(self, *field_names):
- """
- Returns a new QuerySet instance that will select only distinct results.
- """
- assert self.query.can_filter(), \
- "Cannot create distinct fields once a slice has been taken."
- obj = self._clone()
- obj.query.add_distinct_fields(*field_names)
- return obj
-
- def extra(self, select=None, where=None, params=None, tables=None,
- order_by=None, select_params=None):
- """
- Adds extra SQL fragments to the query.
- """
- assert self.query.can_filter(), \
- "Cannot change a query once a slice has been taken"
- clone = self._clone()
- clone.query.add_extra(select, select_params, where, params, tables, order_by)
- return clone
-
- def reverse(self):
- """
- Reverses the ordering of the QuerySet.
- """
- clone = self._clone()
- clone.query.standard_ordering = not clone.query.standard_ordering
- return clone
-
- def defer(self, *fields):
- """
- Defers the loading of data for certain fields until they are accessed.
- The set of fields to defer is added to any existing set of deferred
- fields. The only exception to this is if None is passed in as the only
- parameter, in which case all deferrals are removed (None acts as a
- reset option).
- """
- clone = self._clone()
- if fields == (None,):
- clone.query.clear_deferred_loading()
- else:
- clone.query.add_deferred_loading(fields)
- return clone
-
- def only(self, *fields):
- """
- Essentially, the opposite of defer. Only the fields passed into this
- method and that are not already specified as deferred are loaded
- immediately when the queryset is evaluated.
- """
- if fields == (None,):
- # Can only pass None to defer(), not only(), as the rest option.
- # That won't stop people trying to do this, so let's be explicit.
- raise TypeError("Cannot pass None as an argument to only().")
- clone = self._clone()
- clone.query.add_immediate_loading(fields)
- return clone
-
- def using(self, alias):
- """
- Selects which database this QuerySet should excecute its query against.
- """
- clone = self._clone()
- clone._db = alias
- return clone
-
- ###################################
- # PUBLIC INTROSPECTION ATTRIBUTES #
- ###################################
-
- def ordered(self):
- """
- Returns True if the QuerySet is ordered -- i.e. has an order_by()
- clause or a default ordering on the model.
- """
- if self.query.extra_order_by or self.query.order_by:
- return True
- elif self.query.default_ordering and self.query.get_meta().ordering:
- return True
- else:
- return False
- ordered = property(ordered)
-
- @property
- def db(self):
- "Return the database that will be used if this query is executed now"
- if self._for_write:
- return self._db or router.db_for_write(self.model)
- return self._db or router.db_for_read(self.model)
-
- ###################
- # PRIVATE METHODS #
- ###################
- def _batched_insert(self, objs, fields, batch_size):
- """
- A little helper method for bulk_insert to insert the bulk one batch
- at a time. Inserts recursively a batch from the front of the bulk and
- then _batched_insert() the remaining objects again.
- """
- if not objs:
- return
- ops = connections[self.db].ops
- batch_size = (batch_size or max(ops.bulk_batch_size(fields, objs), 1))
- for batch in [objs[i:i+batch_size]
- for i in range(0, len(objs), batch_size)]:
- self.model._base_manager._insert(batch, fields=fields,
- using=self.db)
-
- def _clone(self, klass=None, setup=False, **kwargs):
- if klass is None:
- klass = self.__class__
- query = self.query.clone()
- if self._sticky_filter:
- query.filter_is_sticky = True
- c = klass(model=self.model, query=query, using=self._db)
- c._for_write = self._for_write
- c._prefetch_related_lookups = self._prefetch_related_lookups[:]
- c._known_related_objects = self._known_related_objects
- c.__dict__.update(kwargs)
- if setup and hasattr(c, '_setup_query'):
- c._setup_query()
- return c
-
- def _fetch_all(self):
- if self._result_cache is None:
- self._result_cache = list(self.iterator())
- if self._prefetch_related_lookups and not self._prefetch_done:
- self._prefetch_related_objects()
-
- def _next_is_sticky(self):
- """
- Indicates that the next filter call and the one following that should
- be treated as a single filter. This is only important when it comes to
- determining when to reuse tables for many-to-many filters. Required so
- that we can filter naturally on the results of related managers.
-
- This doesn't return a clone of the current QuerySet (it returns
- "self"). The method is only used internally and should be immediately
- followed by a filter() that does create a clone.
- """
- self._sticky_filter = True
- return self
-
- def _merge_sanity_check(self, other):
- """
- Checks that we are merging two comparable QuerySet classes. By default
- this does nothing, but see the ValuesQuerySet for an example of where
- it's useful.
- """
- pass
-
- def _merge_known_related_objects(self, other):
- """
- Keep track of all known related objects from either QuerySet instance.
- """
- for field, objects in other._known_related_objects.items():
- self._known_related_objects.setdefault(field, {}).update(objects)
-
- def _setup_aggregate_query(self, aggregates):
- """
- Prepare the query for computing a result that contains aggregate annotations.
- """
- opts = self.model._meta
- if self.query.group_by is None:
- field_names = [f.attname for f in opts.concrete_fields]
- self.query.add_fields(field_names, False)
- self.query.set_group_by()
-
- def _prepare(self):
- return self
-
- def _as_sql(self, connection):
- """
- Returns the internal query's SQL and parameters (as a tuple).
- """
- obj = self.values("pk")
- if obj._db is None or connection == connections[obj._db]:
- return obj.query.get_compiler(connection=connection).as_nested_sql()
- raise ValueError("Can't do subqueries with queries on different DBs.")
-
- # When used as part of a nested query, a queryset will never be an "always
- # empty" result.
- value_annotation = True
-
-class InstanceCheckMeta(type):
- def __instancecheck__(self, instance):
- return instance.query.is_empty()
-
-class EmptyQuerySet(six.with_metaclass(InstanceCheckMeta)):
- """
- Marker class usable for checking if a queryset is empty by .none():
- isinstance(qs.none(), EmptyQuerySet) -> True
- """
-
- def __init__(self, *args, **kwargs):
- raise TypeError("EmptyQuerySet can't be instantiated")
-
-class ValuesQuerySet(QuerySet):
- def __init__(self, *args, **kwargs):
- super(ValuesQuerySet, self).__init__(*args, **kwargs)
- # select_related isn't supported in values(). (FIXME -#3358)
- self.query.select_related = False
-
- # QuerySet.clone() will also set up the _fields attribute with the
- # names of the model fields to select.
-
- def iterator(self):
- # Purge any extra columns that haven't been explicitly asked for
- extra_names = list(self.query.extra_select)
- field_names = self.field_names
- aggregate_names = list(self.query.aggregate_select)
-
- names = extra_names + field_names + aggregate_names
-
- for row in self.query.get_compiler(self.db).results_iter():
- yield dict(zip(names, row))
-
- def delete(self):
- # values().delete() doesn't work currently - make sure it raises an
- # user friendly error.
- raise TypeError("Queries with .values() or .values_list() applied "
- "can't be deleted")
-
- def _setup_query(self):
- """
- Constructs the field_names list that the values query will be
- retrieving.
-
- Called by the _clone() method after initializing the rest of the
- instance.
- """
- self.query.clear_deferred_loading()
- self.query.clear_select_fields()
-
- if self._fields:
- self.extra_names = []
- self.aggregate_names = []
- if not self.query.extra and not self.query.aggregates:
- # Short cut - if there are no extra or aggregates, then
- # the values() clause must be just field names.
- self.field_names = list(self._fields)
- else:
- self.query.default_cols = False
- self.field_names = []
- for f in self._fields:
- # we inspect the full extra_select list since we might
- # be adding back an extra select item that we hadn't
- # had selected previously.
- if f in self.query.extra:
- self.extra_names.append(f)
- elif f in self.query.aggregate_select:
- self.aggregate_names.append(f)
- else:
- self.field_names.append(f)
- else:
- # Default to all fields.
- self.extra_names = None
- self.field_names = [f.attname for f in self.model._meta.concrete_fields]
- self.aggregate_names = None
-
- self.query.select = []
- if self.extra_names is not None:
- self.query.set_extra_mask(self.extra_names)
- self.query.add_fields(self.field_names, True)
- if self.aggregate_names is not None:
- self.query.set_aggregate_mask(self.aggregate_names)
-
- def _clone(self, klass=None, setup=False, **kwargs):
- """
- Cloning a ValuesQuerySet preserves the current fields.
- """
- c = super(ValuesQuerySet, self)._clone(klass, **kwargs)
- if not hasattr(c, '_fields'):
- # Only clone self._fields if _fields wasn't passed into the cloning
- # call directly.
- c._fields = self._fields[:]
- c.field_names = self.field_names
- c.extra_names = self.extra_names
- c.aggregate_names = self.aggregate_names
- if setup and hasattr(c, '_setup_query'):
- c._setup_query()
- return c
-
- def _merge_sanity_check(self, other):
- super(ValuesQuerySet, self)._merge_sanity_check(other)
- if (set(self.extra_names) != set(other.extra_names) or
- set(self.field_names) != set(other.field_names) or
- self.aggregate_names != other.aggregate_names):
- raise TypeError("Merging '%s' classes must involve the same values in each case."
- % self.__class__.__name__)
-
- def _setup_aggregate_query(self, aggregates):
- """
- Prepare the query for computing a result that contains aggregate annotations.
- """
- self.query.set_group_by()
-
- if self.aggregate_names is not None:
- self.aggregate_names.extend(aggregates)
- self.query.set_aggregate_mask(self.aggregate_names)
-
- super(ValuesQuerySet, self)._setup_aggregate_query(aggregates)
-
- def _as_sql(self, connection):
- """
- For ValuesQuerySet (and subclasses like ValuesListQuerySet), they can
- only be used as nested queries if they're already set up to select only
- a single field (in which case, that is the field column that is
- returned). This differs from QuerySet.as_sql(), where the column to
- select is set up by Django.
- """
- if ((self._fields and len(self._fields) > 1) or
- (not self._fields and len(self.model._meta.fields) > 1)):
- raise TypeError('Cannot use a multi-field %s as a filter value.'
- % self.__class__.__name__)
-
- obj = self._clone()
- if obj._db is None or connection == connections[obj._db]:
- return obj.query.get_compiler(connection=connection).as_nested_sql()
- raise ValueError("Can't do subqueries with queries on different DBs.")
-
- def _prepare(self):
- """
- Validates that we aren't trying to do a query like
- value__in=qs.values('value1', 'value2'), which isn't valid.
- """
- if ((self._fields and len(self._fields) > 1) or
- (not self._fields and len(self.model._meta.fields) > 1)):
- raise TypeError('Cannot use a multi-field %s as a filter value.'
- % self.__class__.__name__)
- return self
-
-
-class ValuesListQuerySet(ValuesQuerySet):
- def iterator(self):
- if self.flat and len(self._fields) == 1:
- for row in self.query.get_compiler(self.db).results_iter():
- yield row[0]
- elif not self.query.extra_select and not self.query.aggregate_select:
- for row in self.query.get_compiler(self.db).results_iter():
- yield tuple(row)
- else:
- # When extra(select=...) or an annotation is involved, the extra
- # cols are always at the start of the row, and we need to reorder
- # the fields to match the order in self._fields.
- extra_names = list(self.query.extra_select)
- field_names = self.field_names
- aggregate_names = list(self.query.aggregate_select)
-
- names = extra_names + field_names + aggregate_names
-
- # If a field list has been specified, use it. Otherwise, use the
- # full list of fields, including extras and aggregates.
- if self._fields:
- fields = list(self._fields) + [f for f in aggregate_names if f not in self._fields]
- else:
- fields = names
-
- for row in self.query.get_compiler(self.db).results_iter():
- data = dict(zip(names, row))
- yield tuple([data[f] for f in fields])
-
- def _clone(self, *args, **kwargs):
- clone = super(ValuesListQuerySet, self)._clone(*args, **kwargs)
- if not hasattr(clone, "flat"):
- # Only assign flat if the clone didn't already get it from kwargs
- clone.flat = self.flat
- return clone
-
-
-class DateQuerySet(QuerySet):
- def iterator(self):
- return self.query.get_compiler(self.db).results_iter()
-
- def _setup_query(self):
- """
- Sets up any special features of the query attribute.
-
- Called by the _clone() method after initializing the rest of the
- instance.
- """
- self.query.clear_deferred_loading()
- self.query = self.query.clone(klass=sql.DateQuery, setup=True)
- self.query.select = []
- self.query.add_select(self._field_name, self._kind, self._order)
-
- def _clone(self, klass=None, setup=False, **kwargs):
- c = super(DateQuerySet, self)._clone(klass, False, **kwargs)
- c._field_name = self._field_name
- c._kind = self._kind
- if setup and hasattr(c, '_setup_query'):
- c._setup_query()
- return c
-
-
-class DateTimeQuerySet(QuerySet):
- def iterator(self):
- return self.query.get_compiler(self.db).results_iter()
-
- def _setup_query(self):
- """
- Sets up any special features of the query attribute.
-
- Called by the _clone() method after initializing the rest of the
- instance.
- """
- self.query.clear_deferred_loading()
- self.query = self.query.clone(klass=sql.DateTimeQuery, setup=True, tzinfo=self._tzinfo)
- self.query.select = []
- self.query.add_select(self._field_name, self._kind, self._order)
-
- def _clone(self, klass=None, setup=False, **kwargs):
- c = super(DateTimeQuerySet, self)._clone(klass, False, **kwargs)
- c._field_name = self._field_name
- c._kind = self._kind
- c._tzinfo = self._tzinfo
- if setup and hasattr(c, '_setup_query'):
- c._setup_query()
- return c
-
-
-def get_klass_info(klass, max_depth=0, cur_depth=0, requested=None,
- only_load=None, from_parent=None):
- """
- Helper function that recursively returns an information for a klass, to be
- used in get_cached_row. It exists just to compute this information only
- once for entire queryset. Otherwise it would be computed for each row, which
- leads to poor perfomance on large querysets.
-
- Arguments:
- * klass - the class to retrieve (and instantiate)
- * max_depth - the maximum depth to which a select_related()
- relationship should be explored.
- * cur_depth - the current depth in the select_related() tree.
- Used in recursive calls to determin if we should dig deeper.
- * requested - A dictionary describing the select_related() tree
- that is to be retrieved. keys are field names; values are
- dictionaries describing the keys on that related object that
- are themselves to be select_related().
- * only_load - if the query has had only() or defer() applied,
- this is the list of field names that will be returned. If None,
- the full field list for `klass` can be assumed.
- * from_parent - the parent model used to get to this model
-
- Note that when travelling from parent to child, we will only load child
- fields which aren't in the parent.
- """
- if max_depth and requested is None and cur_depth > max_depth:
- # We've recursed deeply enough; stop now.
- return None
-
- if only_load:
- load_fields = only_load.get(klass) or set()
- # When we create the object, we will also be creating populating
- # all the parent classes, so traverse the parent classes looking
- # for fields that must be included on load.
- for parent in klass._meta.get_parent_list():
- fields = only_load.get(parent)
- if fields:
- load_fields.update(fields)
- else:
- load_fields = None
-
- if load_fields:
- # Handle deferred fields.
- skip = set()
- init_list = []
- # Build the list of fields that *haven't* been requested
- for field, model in klass._meta.get_concrete_fields_with_model():
- if field.name not in load_fields:
- skip.add(field.attname)
- elif from_parent and issubclass(from_parent, model.__class__):
- # Avoid loading fields already loaded for parent model for
- # child models.
- continue
- else:
- init_list.append(field.attname)
- # Retrieve all the requested fields
- field_count = len(init_list)
- if skip:
- klass = deferred_class_factory(klass, skip)
- field_names = init_list
- else:
- field_names = ()
- else:
- # Load all fields on klass
-
- field_count = len(klass._meta.concrete_fields)
- # Check if we need to skip some parent fields.
- if from_parent and len(klass._meta.local_concrete_fields) != len(klass._meta.concrete_fields):
- # Only load those fields which haven't been already loaded into
- # 'from_parent'.
- non_seen_models = [p for p in klass._meta.get_parent_list()
- if not issubclass(from_parent, p)]
- # Load local fields, too...
- non_seen_models.append(klass)
- field_names = [f.attname for f in klass._meta.concrete_fields
- if f.model in non_seen_models]
- field_count = len(field_names)
- # Try to avoid populating field_names variable for perfomance reasons.
- # If field_names variable is set, we use **kwargs based model init
- # which is slower than normal init.
- if field_count == len(klass._meta.concrete_fields):
- field_names = ()
-
- restricted = requested is not None
-
- related_fields = []
- for f in klass._meta.fields:
- if select_related_descend(f, restricted, requested, load_fields):
- if restricted:
- next = requested[f.name]
- else:
- next = None
- klass_info = get_klass_info(f.rel.to, max_depth=max_depth, cur_depth=cur_depth+1,
- requested=next, only_load=only_load)
- related_fields.append((f, klass_info))
-
- reverse_related_fields = []
- if restricted:
- for o in klass._meta.get_all_related_objects():
- if o.field.unique and select_related_descend(o.field, restricted, requested,
- only_load.get(o.model), reverse=True):
- next = requested[o.field.related_query_name()]
- parent = klass if issubclass(o.model, klass) else None
- klass_info = get_klass_info(o.model, max_depth=max_depth, cur_depth=cur_depth+1,
- requested=next, only_load=only_load, from_parent=parent)
- reverse_related_fields.append((o.field, klass_info))
- if field_names:
- pk_idx = field_names.index(klass._meta.pk.attname)
- else:
- pk_idx = klass._meta.pk_index()
-
- return klass, field_names, field_count, related_fields, reverse_related_fields, pk_idx
-
-
-def get_cached_row(row, index_start, using, klass_info, offset=0,
- parent_data=()):
- """
- Helper function that recursively returns an object with the specified
- related attributes already populated.
-
- This method may be called recursively to populate deep select_related()
- clauses.
-
- Arguments:
- * row - the row of data returned by the database cursor
- * index_start - the index of the row at which data for this
- object is known to start
- * offset - the number of additional fields that are known to
- exist in row for `klass`. This usually means the number of
- annotated results on `klass`.
- * using - the database alias on which the query is being executed.
- * klass_info - result of the get_klass_info function
- * parent_data - parent model data in format (field, value). Used
- to populate the non-local fields of child models.
- """
- if klass_info is None:
- return None
- klass, field_names, field_count, related_fields, reverse_related_fields, pk_idx = klass_info
-
-
- fields = row[index_start : index_start + field_count]
- # If the pk column is None (or the Oracle equivalent ''), then the related
- # object must be non-existent - set the relation to None.
- if fields[pk_idx] == None or fields[pk_idx] == '':
- obj = None
- elif field_names:
- fields = list(fields)
- for rel_field, value in parent_data:
- field_names.append(rel_field.attname)
- fields.append(value)
- obj = klass(**dict(zip(field_names, fields)))
- else:
- obj = klass(*fields)
- # If an object was retrieved, set the database state.
- if obj:
- obj._state.db = using
- obj._state.adding = False
-
- # Instantiate related fields
- index_end = index_start + field_count + offset
- # Iterate over each related object, populating any
- # select_related() fields
- for f, klass_info in related_fields:
- # Recursively retrieve the data for the related object
- cached_row = get_cached_row(row, index_end, using, klass_info)
- # If the recursive descent found an object, populate the
- # descriptor caches relevant to the object
- if cached_row:
- rel_obj, index_end = cached_row
- if obj is not None:
- # If the base object exists, populate the
- # descriptor cache
- setattr(obj, f.get_cache_name(), rel_obj)
- if f.unique and rel_obj is not None:
- # If the field is unique, populate the
- # reverse descriptor cache on the related object
- setattr(rel_obj, f.related.get_cache_name(), obj)
-
- # Now do the same, but for reverse related objects.
- # Only handle the restricted case - i.e., don't do a depth
- # descent into reverse relations unless explicitly requested
- for f, klass_info in reverse_related_fields:
- # Transfer data from this object to childs.
- parent_data = []
- for rel_field, rel_model in klass_info[0]._meta.get_fields_with_model():
- if rel_model is not None and isinstance(obj, rel_model):
- parent_data.append((rel_field, getattr(obj, rel_field.attname)))
- # Recursively retrieve the data for the related object
- cached_row = get_cached_row(row, index_end, using, klass_info,
- parent_data=parent_data)
- # If the recursive descent found an object, populate the
- # descriptor caches relevant to the object
- if cached_row:
- rel_obj, index_end = cached_row
- if obj is not None:
- # populate the reverse descriptor cache
- setattr(obj, f.related.get_cache_name(), rel_obj)
- if rel_obj is not None:
- # If the related object exists, populate
- # the descriptor cache.
- setattr(rel_obj, f.get_cache_name(), obj)
- # Populate related object caches using parent data.
- for rel_field, _ in parent_data:
- if rel_field.rel:
- setattr(rel_obj, rel_field.attname, getattr(obj, rel_field.attname))
- try:
- cached_obj = getattr(obj, rel_field.get_cache_name())
- setattr(rel_obj, rel_field.get_cache_name(), cached_obj)
- except AttributeError:
- # Related object hasn't been cached yet
- pass
- return obj, index_end
-
-
-class RawQuerySet(object):
- """
- Provides an iterator which converts the results of raw SQL queries into
- annotated model instances.
- """
- def __init__(self, raw_query, model=None, query=None, params=None,
- translations=None, using=None):
- self.raw_query = raw_query
- self.model = model
- self._db = using
- self.query = query or sql.RawQuery(sql=raw_query, using=self.db, params=params)
- self.params = params or ()
- self.translations = translations or {}
-
- def __iter__(self):
- # Mapping of attrnames to row column positions. Used for constructing
- # the model using kwargs, needed when not all model's fields are present
- # in the query.
- model_init_field_names = {}
- # A list of tuples of (column name, column position). Used for
- # annotation fields.
- annotation_fields = []
-
- # Cache some things for performance reasons outside the loop.
- db = self.db
- compiler = connections[db].ops.compiler('SQLCompiler')(
- self.query, connections[db], db
- )
- need_resolv_columns = hasattr(compiler, 'resolve_columns')
-
- query = iter(self.query)
-
- # Find out which columns are model's fields, and which ones should be
- # annotated to the model.
- for pos, column in enumerate(self.columns):
- if column in self.model_fields:
- model_init_field_names[self.model_fields[column].attname] = pos
- else:
- annotation_fields.append((column, pos))
-
- # Find out which model's fields are not present in the query.
- skip = set()
- for field in self.model._meta.fields:
- if field.attname not in model_init_field_names:
- skip.add(field.attname)
- if skip:
- if self.model._meta.pk.attname in skip:
- raise InvalidQuery('Raw query must include the primary key')
- model_cls = deferred_class_factory(self.model, skip)
- else:
- model_cls = self.model
- # All model's fields are present in the query. So, it is possible
- # to use *args based model instantation. For each field of the model,
- # record the query column position matching that field.
- model_init_field_pos = []
- for field in self.model._meta.fields:
- model_init_field_pos.append(model_init_field_names[field.attname])
- if need_resolv_columns:
- fields = [self.model_fields.get(c, None) for c in self.columns]
- # Begin looping through the query values.
- for values in query:
- if need_resolv_columns:
- values = compiler.resolve_columns(values, fields)
- # Associate fields to values
- if skip:
- model_init_kwargs = {}
- for attname, pos in six.iteritems(model_init_field_names):
- model_init_kwargs[attname] = values[pos]
- instance = model_cls(**model_init_kwargs)
- else:
- model_init_args = [values[pos] for pos in model_init_field_pos]
- instance = model_cls(*model_init_args)
- if annotation_fields:
- for column, pos in annotation_fields:
- setattr(instance, column, values[pos])
-
- instance._state.db = db
- instance._state.adding = False
-
- yield instance
-
- def __repr__(self):
- text = self.raw_query
- if self.params:
- text = text % (self.params if hasattr(self.params, 'keys') else tuple(self.params))
- return "<RawQuerySet: %r>" % text
-
- def __getitem__(self, k):
- return list(self)[k]
-
- @property
- def db(self):
- "Return the database that will be used if this query is executed now"
- return self._db or router.db_for_read(self.model)
-
- def using(self, alias):
- """
- Selects which database this Raw QuerySet should excecute it's query against.
- """
- return RawQuerySet(self.raw_query, model=self.model,
- query=self.query.clone(using=alias),
- params=self.params, translations=self.translations,
- using=alias)
-
- @property
- def columns(self):
- """
- A list of model field names in the order they'll appear in the
- query results.
- """
- if not hasattr(self, '_columns'):
- self._columns = self.query.get_columns()
-
- # Adjust any column names which don't match field names
- for (query_name, model_name) in self.translations.items():
- try:
- index = self._columns.index(query_name)
- self._columns[index] = model_name
- except ValueError:
- # Ignore translations for non-existant column names
- pass
-
- return self._columns
-
- @property
- def model_fields(self):
- """
- A dict mapping column names to model field names.
- """
- if not hasattr(self, '_model_fields'):
- converter = connections[self.db].introspection.table_name_converter
- self._model_fields = {}
- for field in self.model._meta.fields:
- name, column = field.get_attname_column()
- self._model_fields[converter(column)] = field
- return self._model_fields
-
-
-def insert_query(model, objs, fields, return_id=False, raw=False, using=None):
- """
- Inserts a new record for the given model. This provides an interface to
- the InsertQuery class and is how Model.save() is implemented. It is not
- part of the public API.
- """
- query = sql.InsertQuery(model)
- query.insert_values(fields, objs, raw=raw)
- return query.get_compiler(using=using).execute_sql(return_id)
-
-
-def prefetch_related_objects(result_cache, related_lookups):
- """
- Helper function for prefetch_related functionality
-
- Populates prefetched objects caches for a list of results
- from a QuerySet
- """
- if len(result_cache) == 0:
- return # nothing to do
-
- model = result_cache[0].__class__
-
- # We need to be able to dynamically add to the list of prefetch_related
- # lookups that we look up (see below). So we need some book keeping to
- # ensure we don't do duplicate work.
- done_lookups = set() # list of lookups like foo__bar__baz
- done_queries = {} # dictionary of things like 'foo__bar': [results]
-
- auto_lookups = [] # we add to this as we go through.
- followed_descriptors = set() # recursion protection
-
- all_lookups = itertools.chain(related_lookups, auto_lookups)
- for lookup in all_lookups:
- if lookup in done_lookups:
- # We've done exactly this already, skip the whole thing
- continue
- done_lookups.add(lookup)
-
- # Top level, the list of objects to decorate is the result cache
- # from the primary QuerySet. It won't be for deeper levels.
- obj_list = result_cache
-
- attrs = lookup.split(LOOKUP_SEP)
- for level, attr in enumerate(attrs):
- # Prepare main instances
- if len(obj_list) == 0:
- break
-
- current_lookup = LOOKUP_SEP.join(attrs[0:level+1])
- if current_lookup in done_queries:
- # Skip any prefetching, and any object preparation
- obj_list = done_queries[current_lookup]
- continue
-
- # Prepare objects:
- good_objects = True
- for obj in obj_list:
- # Since prefetching can re-use instances, it is possible to have
- # the same instance multiple times in obj_list, so obj might
- # already be prepared.
- if not hasattr(obj, '_prefetched_objects_cache'):
- try:
- obj._prefetched_objects_cache = {}
- except AttributeError:
- # Must be in a QuerySet subclass that is not returning
- # Model instances, either in Django or 3rd
- # party. prefetch_related() doesn't make sense, so quit
- # now.
- good_objects = False
- break
- if not good_objects:
- break
-
- # Descend down tree
-
- # We assume that objects retrieved are homogenous (which is the premise
- # of prefetch_related), so what applies to first object applies to all.
- first_obj = obj_list[0]
- prefetcher, descriptor, attr_found, is_fetched = get_prefetcher(first_obj, attr)
-
- if not attr_found:
- raise AttributeError("Cannot find '%s' on %s object, '%s' is an invalid "
- "parameter to prefetch_related()" %
- (attr, first_obj.__class__.__name__, lookup))
-
- if level == len(attrs) - 1 and prefetcher is None:
- # Last one, this *must* resolve to something that supports
- # prefetching, otherwise there is no point adding it and the
- # developer asking for it has made a mistake.
- raise ValueError("'%s' does not resolve to a item that supports "
- "prefetching - this is an invalid parameter to "
- "prefetch_related()." % lookup)
-
- if prefetcher is not None and not is_fetched:
- obj_list, additional_prl = prefetch_one_level(obj_list, prefetcher, attr)
- # We need to ensure we don't keep adding lookups from the
- # same relationships to stop infinite recursion. So, if we
- # are already on an automatically added lookup, don't add
- # the new lookups from relationships we've seen already.
- if not (lookup in auto_lookups and
- descriptor in followed_descriptors):
- for f in additional_prl:
- new_prl = LOOKUP_SEP.join([current_lookup, f])
- auto_lookups.append(new_prl)
- done_queries[current_lookup] = obj_list
- followed_descriptors.add(descriptor)
- else:
- # Either a singly related object that has already been fetched
- # (e.g. via select_related), or hopefully some other property
- # that doesn't support prefetching but needs to be traversed.
-
- # We replace the current list of parent objects with the list
- # of related objects, filtering out empty or missing values so
- # that we can continue with nullable or reverse relations.
- new_obj_list = []
- for obj in obj_list:
- try:
- new_obj = getattr(obj, attr)
- except exceptions.ObjectDoesNotExist:
- continue
- if new_obj is None:
- continue
- new_obj_list.append(new_obj)
- obj_list = new_obj_list
-
-
-def get_prefetcher(instance, attr):
- """
- For the attribute 'attr' on the given instance, finds
- an object that has a get_prefetch_queryset().
- Returns a 4 tuple containing:
- (the object with get_prefetch_queryset (or None),
- the descriptor object representing this relationship (or None),
- a boolean that is False if the attribute was not found at all,
- a boolean that is True if the attribute has already been fetched)
- """
- prefetcher = None
- attr_found = False
- is_fetched = False
-
- # For singly related objects, we have to avoid getting the attribute
- # from the object, as this will trigger the query. So we first try
- # on the class, in order to get the descriptor object.
- rel_obj_descriptor = getattr(instance.__class__, attr, None)
- if rel_obj_descriptor is None:
- try:
- rel_obj = getattr(instance, attr)
- attr_found = True
- except AttributeError:
- pass
- else:
- attr_found = True
- if rel_obj_descriptor:
- # singly related object, descriptor object has the
- # get_prefetch_queryset() method.
- if hasattr(rel_obj_descriptor, 'get_prefetch_queryset'):
- prefetcher = rel_obj_descriptor
- if rel_obj_descriptor.is_cached(instance):
- is_fetched = True
- else:
- # descriptor doesn't support prefetching, so we go ahead and get
- # the attribute on the instance rather than the class to
- # support many related managers
- rel_obj = getattr(instance, attr)
- if hasattr(rel_obj, 'get_prefetch_queryset'):
- prefetcher = rel_obj
- return prefetcher, rel_obj_descriptor, attr_found, is_fetched
-
-
-def prefetch_one_level(instances, prefetcher, attname):
- """
- Helper function for prefetch_related_objects
-
- Runs prefetches on all instances using the prefetcher object,
- assigning results to relevant caches in instance.
-
- The prefetched objects are returned, along with any additional
- prefetches that must be done due to prefetch_related lookups
- found from default managers.
- """
- # prefetcher must have a method get_prefetch_queryset() which takes a list
- # of instances, and returns a tuple:
-
- # (queryset of instances of self.model that are related to passed in instances,
- # callable that gets value to be matched for returned instances,
- # callable that gets value to be matched for passed in instances,
- # boolean that is True for singly related objects,
- # cache name to assign to).
-
- # The 'values to be matched' must be hashable as they will be used
- # in a dictionary.
-
- rel_qs, rel_obj_attr, instance_attr, single, cache_name =\
- prefetcher.get_prefetch_queryset(instances)
- # We have to handle the possibility that the default manager itself added
- # prefetch_related lookups to the QuerySet we just got back. We don't want to
- # trigger the prefetch_related functionality by evaluating the query.
- # Rather, we need to merge in the prefetch_related lookups.
- additional_prl = getattr(rel_qs, '_prefetch_related_lookups', [])
- if additional_prl:
- # Don't need to clone because the manager should have given us a fresh
- # instance, so we access an internal instead of using public interface
- # for performance reasons.
- rel_qs._prefetch_related_lookups = []
-
- all_related_objects = list(rel_qs)
-
- rel_obj_cache = {}
- for rel_obj in all_related_objects:
- rel_attr_val = rel_obj_attr(rel_obj)
- rel_obj_cache.setdefault(rel_attr_val, []).append(rel_obj)
-
- for obj in instances:
- instance_attr_val = instance_attr(obj)
- vals = rel_obj_cache.get(instance_attr_val, [])
- if single:
- # Need to assign to single cache on instance
- setattr(obj, cache_name, vals[0] if vals else None)
- else:
- # Multi, attribute represents a manager with an .all() method that
- # returns a QuerySet
- qs = getattr(obj, attname).all()
- qs._result_cache = vals
- # We don't want the individual qs doing prefetch_related now, since we
- # have merged this into the current work.
- qs._prefetch_done = True
- obj._prefetched_objects_cache[cache_name] = qs
- return all_related_objects, additional_prl