| 1 |
from django.conf import settings |
|---|
| 2 |
from django.db import backend, connection, transaction |
|---|
| 3 |
from django.db.models.fields import DateField, FieldDoesNotExist |
|---|
| 4 |
from django.db.models import signals, loading |
|---|
| 5 |
from django.dispatch import dispatcher |
|---|
| 6 |
from django.utils.datastructures import SortedDict |
|---|
| 7 |
from django.utils.encoding import smart_unicode |
|---|
| 8 |
from django.contrib.contenttypes import generic |
|---|
| 9 |
import datetime |
|---|
| 10 |
import operator |
|---|
| 11 |
import re |
|---|
| 12 |
|
|---|
| 13 |
try: |
|---|
| 14 |
set |
|---|
| 15 |
except NameError: |
|---|
| 16 |
from sets import Set as set # Python 2.3 fallback |
|---|
| 17 |
|
|---|
| 18 |
# The string constant used to separate query parts |
|---|
| 19 |
LOOKUP_SEPARATOR = '__' |
|---|
| 20 |
|
|---|
| 21 |
# The list of valid query types |
|---|
| 22 |
QUERY_TERMS = ( |
|---|
| 23 |
'exact', 'iexact', 'contains', 'icontains', |
|---|
| 24 |
'gt', 'gte', 'lt', 'lte', 'in', |
|---|
| 25 |
'startswith', 'istartswith', 'endswith', 'iendswith', |
|---|
| 26 |
'range', 'year', 'month', 'day', 'isnull', 'search', |
|---|
| 27 |
'regex', 'iregex', |
|---|
| 28 |
) |
|---|
| 29 |
|
|---|
| 30 |
# Size of each "chunk" for get_iterator calls. |
|---|
| 31 |
# Larger values are slightly faster at the expense of more storage space. |
|---|
| 32 |
GET_ITERATOR_CHUNK_SIZE = 100 |
|---|
| 33 |
|
|---|
| 34 |
class EmptyResultSet(Exception): |
|---|
| 35 |
pass |
|---|
| 36 |
|
|---|
| 37 |
#################### |
|---|
| 38 |
# HELPER FUNCTIONS # |
|---|
| 39 |
#################### |
|---|
| 40 |
|
|---|
| 41 |
# Django currently supports two forms of ordering. |
|---|
| 42 |
# Form 1 (deprecated) example: |
|---|
| 43 |
# order_by=(('pub_date', 'DESC'), ('headline', 'ASC'), (None, 'RANDOM')) |
|---|
| 44 |
# Form 2 (new-style) example: |
|---|
| 45 |
# order_by=('-pub_date', 'headline', '?') |
|---|
| 46 |
# Form 1 is deprecated and will no longer be supported for Django's first |
|---|
| 47 |
# official release. The following code converts from Form 1 to Form 2. |
|---|
| 48 |
|
|---|
| 49 |
LEGACY_ORDERING_MAPPING = {'ASC': '_', 'DESC': '-_', 'RANDOM': '?'} |
|---|
| 50 |
|
|---|
| 51 |
def handle_legacy_orderlist(order_list): |
|---|
| 52 |
if not order_list or isinstance(order_list[0], basestring): |
|---|
| 53 |
return order_list |
|---|
| 54 |
else: |
|---|
| 55 |
import warnings |
|---|
| 56 |
new_order_list = [LEGACY_ORDERING_MAPPING[j.upper()].replace('_', smart_unicode(i)) for i, j in order_list] |
|---|
| 57 |
warnings.warn("%r ordering syntax is deprecated. Use %r instead." % (order_list, new_order_list), DeprecationWarning) |
|---|
| 58 |
return new_order_list |
|---|
| 59 |
|
|---|
| 60 |
def orderfield2column(f, opts): |
|---|
| 61 |
try: |
|---|
| 62 |
return opts.get_field(f, False).column |
|---|
| 63 |
except FieldDoesNotExist: |
|---|
| 64 |
return f |
|---|
| 65 |
|
|---|
| 66 |
def orderlist2sql(order_list, opts, prefix=''): |
|---|
| 67 |
if prefix.endswith('.'): |
|---|
| 68 |
prefix = backend.quote_name(prefix[:-1]) + '.' |
|---|
| 69 |
output = [] |
|---|
| 70 |
for f in handle_legacy_orderlist(order_list): |
|---|
| 71 |
if f.startswith('-'): |
|---|
| 72 |
output.append('%s%s DESC' % (prefix, backend.quote_name(orderfield2column(f[1:], opts)))) |
|---|
| 73 |
elif f == '?': |
|---|
| 74 |
output.append(backend.get_random_function_sql()) |
|---|
| 75 |
else: |
|---|
| 76 |
output.append('%s%s ASC' % (prefix, backend.quote_name(orderfield2column(f, opts)))) |
|---|
| 77 |
return ', '.join(output) |
|---|
| 78 |
|
|---|
| 79 |
def quote_only_if_word(word): |
|---|
| 80 |
if re.search('\W', word): # Don't quote if there are spaces or non-word chars. |
|---|
| 81 |
return word |
|---|
| 82 |
else: |
|---|
| 83 |
return backend.quote_name(word) |
|---|
| 84 |
|
|---|
| 85 |
class _QuerySet(object): |
|---|
| 86 |
"Represents a lazy database lookup for a set of objects" |
|---|
| 87 |
def __init__(self, model=None): |
|---|
| 88 |
self.model = model |
|---|
| 89 |
self._filters = Q() |
|---|
| 90 |
self._order_by = None # Ordering, e.g. ('date', '-name'). If None, use model's ordering. |
|---|
| 91 |
self._select_related = False # Whether to fill cache for related objects. |
|---|
| 92 |
self._max_related_depth = 0 # Maximum "depth" for select_related |
|---|
| 93 |
self._distinct = False # Whether the query should use SELECT DISTINCT. |
|---|
| 94 |
self._select = {} # Dictionary of attname -> SQL. |
|---|
| 95 |
self._where = [] # List of extra WHERE clauses to use. |
|---|
| 96 |
self._params = [] # List of params to use for extra WHERE clauses. |
|---|
| 97 |
self._tables = [] # List of extra tables to use. |
|---|
| 98 |
self._offset = None # OFFSET clause. |
|---|
| 99 |
self._limit = None # LIMIT clause. |
|---|
| 100 |
self._result_cache = None |
|---|
| 101 |
|
|---|
| 102 |
######################## |
|---|
| 103 |
# PYTHON MAGIC METHODS # |
|---|
| 104 |
######################## |
|---|
| 105 |
|
|---|
| 106 |
def __repr__(self): |
|---|
| 107 |
return repr(self._get_data()) |
|---|
| 108 |
|
|---|
| 109 |
def __len__(self): |
|---|
| 110 |
return len(self._get_data()) |
|---|
| 111 |
|
|---|
| 112 |
def __iter__(self): |
|---|
| 113 |
return iter(self._get_data()) |
|---|
| 114 |
|
|---|
| 115 |
def __getitem__(self, k): |
|---|
| 116 |
"Retrieve an item or slice from the set of results." |
|---|
| 117 |
if not isinstance(k, (slice, int)): |
|---|
| 118 |
raise TypeError |
|---|
| 119 |
assert (not isinstance(k, slice) and (k >= 0)) \ |
|---|
| 120 |
or (isinstance(k, slice) and (k.start is None or k.start >= 0) and (k.stop is None or k.stop >= 0)), \ |
|---|
| 121 |
"Negative indexing is not supported." |
|---|
| 122 |
if self._result_cache is None: |
|---|
| 123 |
if isinstance(k, slice): |
|---|
| 124 |
# Offset: |
|---|
| 125 |
if self._offset is None: |
|---|
| 126 |
offset = k.start |
|---|
| 127 |
elif k.start is None: |
|---|
| 128 |
offset = self._offset |
|---|
| 129 |
else: |
|---|
| 130 |
offset = self._offset + k.start |
|---|
| 131 |
# Now adjust offset to the bounds of any existing limit: |
|---|
| 132 |
if self._limit is not None and k.start is not None: |
|---|
| 133 |
limit = self._limit - k.start |
|---|
| 134 |
else: |
|---|
| 135 |
limit = self._limit |
|---|
| 136 |
|
|---|
| 137 |
# Limit: |
|---|
| 138 |
if k.stop is not None and k.start is not None: |
|---|
| 139 |
if limit is None: |
|---|
| 140 |
limit = k.stop - k.start |
|---|
| 141 |
else: |
|---|
| 142 |
limit = min((k.stop - k.start), limit) |
|---|
| 143 |
else: |
|---|
| 144 |
if limit is None: |
|---|
| 145 |
limit = k.stop |
|---|
| 146 |
else: |
|---|
| 147 |
if k.stop is not None: |
|---|
| 148 |
limit = min(k.stop, limit) |
|---|
| 149 |
|
|---|
| 150 |
if k.step is None: |
|---|
| 151 |
return self._clone(_offset=offset, _limit=limit) |
|---|
| 152 |
else: |
|---|
| 153 |
return list(self._clone(_offset=offset, _limit=limit))[::k.step] |
|---|
| 154 |
else: |
|---|
| 155 |
try: |
|---|
| 156 |
return list(self._clone(_offset=k, _limit=1))[0] |
|---|
| 157 |
except self.model.DoesNotExist, e: |
|---|
| 158 |
raise IndexError, e.args |
|---|
| 159 |
else: |
|---|
| 160 |
return self._result_cache[k] |
|---|
| 161 |
|
|---|
| 162 |
def __and__(self, other): |
|---|
| 163 |
combined = self._combine(other) |
|---|
| 164 |
combined._filters = self._filters & other._filters |
|---|
| 165 |
return combined |
|---|
| 166 |
|
|---|
| 167 |
def __or__(self, other): |
|---|
| 168 |
combined = self._combine(other) |
|---|
| 169 |
combined._filters = self._filters | other._filters |
|---|
| 170 |
return combined |
|---|
| 171 |
|
|---|
| 172 |
#################################### |
|---|
| 173 |
# METHODS THAT DO DATABASE QUERIES # |
|---|
| 174 |
#################################### |
|---|
| 175 |
|
|---|
| 176 |
def iterator(self): |
|---|
| 177 |
"Performs the SELECT database lookup of this QuerySet." |
|---|
| 178 |
try: |
|---|
| 179 |
select, sql, params = self._get_sql_clause() |
|---|
| 180 |
except EmptyResultSet: |
|---|
| 181 |
raise StopIteration |
|---|
| 182 |
|
|---|
| 183 |
# self._select is a dictionary, and dictionaries' key order is |
|---|
| 184 |
# undefined, so we convert it to a list of tuples. |
|---|
| 185 |
extra_select = self._select.items() |
|---|
| 186 |
|
|---|
| 187 |
cursor = connection.cursor() |
|---|
| 188 |
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) |
|---|
| 189 |
|
|---|
| 190 |
fill_cache = self._select_related |
|---|
| 191 |
fields = self.model._meta.fields |
|---|
| 192 |
index_end = len(fields) |
|---|
| 193 |
has_resolve_columns = hasattr(self, 'resolve_columns') |
|---|
| 194 |
while 1: |
|---|
| 195 |
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) |
|---|
| 196 |
if not rows: |
|---|
| 197 |
raise StopIteration |
|---|
| 198 |
for row in rows: |
|---|
| 199 |
if has_resolve_columns: |
|---|
| 200 |
row = self.resolve_columns(row, fields) |
|---|
| 201 |
if fill_cache: |
|---|
| 202 |
obj, index_end = get_cached_row(klass=self.model, row=row, |
|---|
| 203 |
index_start=0, max_depth=self._max_related_depth) |
|---|
| 204 |
else: |
|---|
| 205 |
obj = self.model(*row[:index_end]) |
|---|
| 206 |
for i, k in enumerate(extra_select): |
|---|
| 207 |
setattr(obj, k[0], row[index_end+i]) |
|---|
| 208 |
yield obj |
|---|
| 209 |
|
|---|
| 210 |
def count(self): |
|---|
| 211 |
""" |
|---|
| 212 |
Performs a SELECT COUNT() and returns the number of records as an |
|---|
| 213 |
integer. |
|---|
| 214 |
|
|---|
| 215 |
If the queryset is already cached (i.e. self._result_cache is set) this |
|---|
| 216 |
simply returns the length of the cached results set to avoid multiple |
|---|
| 217 |
SELECT COUNT(*) calls. |
|---|
| 218 |
""" |
|---|
| 219 |
if self._result_cache is not None: |
|---|
| 220 |
return len(self._result_cache) |
|---|
| 221 |
|
|---|
| 222 |
counter = self._clone() |
|---|
| 223 |
counter._order_by = () |
|---|
| 224 |
counter._select_related = False |
|---|
| 225 |
|
|---|
| 226 |
offset = counter._offset |
|---|
| 227 |
limit = counter._limit |
|---|
| 228 |
counter._offset = None |
|---|
| 229 |
counter._limit = None |
|---|
| 230 |
|
|---|
| 231 |
try: |
|---|
| 232 |
select, sql, params = counter._get_sql_clause() |
|---|
| 233 |
except EmptyResultSet: |
|---|
| 234 |
return 0 |
|---|
| 235 |
|
|---|
| 236 |
cursor = connection.cursor() |
|---|
| 237 |
if self._distinct: |
|---|
| 238 |
id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table), |
|---|
| 239 |
backend.quote_name(self.model._meta.pk.column)) |
|---|
| 240 |
cursor.execute("SELECT COUNT(DISTINCT(%s))" % id_col + sql, params) |
|---|
| 241 |
else: |
|---|
| 242 |
cursor.execute("SELECT COUNT(*)" + sql, params) |
|---|
| 243 |
count = cursor.fetchone()[0] |
|---|
| 244 |
|
|---|
| 245 |
# Apply any offset and limit constraints manually, since using LIMIT or |
|---|
| 246 |
# OFFSET in SQL doesn't change the output of COUNT. |
|---|
| 247 |
if offset: |
|---|
| 248 |
count = max(0, count - offset) |
|---|
| 249 |
if limit: |
|---|
| 250 |
count = min(limit, count) |
|---|
| 251 |
|
|---|
| 252 |
return count |
|---|
| 253 |
|
|---|
| 254 |
def get(self, *args, **kwargs): |
|---|
| 255 |
"Performs the SELECT and returns a single object matching the given keyword arguments." |
|---|
| 256 |
clone = self.filter(*args, **kwargs) |
|---|
| 257 |
# clean up SQL by removing unneeded ORDER BY |
|---|
| 258 |
if not clone._order_by: |
|---|
| 259 |
clone._order_by = () |
|---|
| 260 |
obj_list = list(clone) |
|---|
| 261 |
if len(obj_list) < 1: |
|---|
| 262 |
raise self.model.DoesNotExist, "%s matching query does not exist." % self.model._meta.object_name |
|---|
| 263 |
assert len(obj_list) == 1, "get() returned more than one %s -- it returned %s! Lookup parameters were %s" % (self.model._meta.object_name, len(obj_list), kwargs) |
|---|
| 264 |
return obj_list[0] |
|---|
| 265 |
|
|---|
| 266 |
def create(self, **kwargs): |
|---|
| 267 |
""" |
|---|
| 268 |
Create a new object with the given kwargs, saving it to the database |
|---|
| 269 |
and returning the created object. |
|---|
| 270 |
""" |
|---|
| 271 |
obj = self.model(**kwargs) |
|---|
| 272 |
obj.save() |
|---|
| 273 |
return obj |
|---|
| 274 |
|
|---|
| 275 |
def get_or_create(self, **kwargs): |
|---|
| 276 |
""" |
|---|
| 277 |
Looks up an object with the given kwargs, creating one if necessary. |
|---|
| 278 |
Returns a tuple of (object, created), where created is a boolean |
|---|
| 279 |
specifying whether an object was created. |
|---|
| 280 |
""" |
|---|
| 281 |
assert len(kwargs), 'get_or_create() must be passed at least one keyword argument' |
|---|
| 282 |
defaults = kwargs.pop('defaults', {}) |
|---|
| 283 |
try: |
|---|
| 284 |
return self.get(**kwargs), False |
|---|
| 285 |
except self.model.DoesNotExist: |
|---|
| 286 |
params = dict([(k, v) for k, v in kwargs.items() if '__' not in k]) |
|---|
| 287 |
params.update(defaults) |
|---|
| 288 |
obj = self.model(**params) |
|---|
| 289 |
obj.save() |
|---|
| 290 |
return obj, True |
|---|
| 291 |
|
|---|
| 292 |
def latest(self, field_name=None): |
|---|
| 293 |
""" |
|---|
| 294 |
Returns the latest object, according to the model's 'get_latest_by' |
|---|
| 295 |
option or optional given field_name. |
|---|
| 296 |
""" |
|---|
| 297 |
latest_by = field_name or self.model._meta.get_latest_by |
|---|
| 298 |
assert bool(latest_by), "latest() requires either a field_name parameter or 'get_latest_by' in the model" |
|---|
| 299 |
assert self._limit is None and self._offset is None, \ |
|---|
| 300 |
"Cannot change a query once a slice has been taken." |
|---|
| 301 |
return self._clone(_limit=1, _order_by=('-'+latest_by,)).get() |
|---|
| 302 |
|
|---|
| 303 |
def in_bulk(self, id_list): |
|---|
| 304 |
""" |
|---|
| 305 |
Returns a dictionary mapping each of the given IDs to the object with |
|---|
| 306 |
that ID. |
|---|
| 307 |
""" |
|---|
| 308 |
assert self._limit is None and self._offset is None, \ |
|---|
| 309 |
"Cannot use 'limit' or 'offset' with in_bulk" |
|---|
| 310 |
assert isinstance(id_list, (tuple, list)), "in_bulk() must be provided with a list of IDs." |
|---|
| 311 |
id_list = list(id_list) |
|---|
| 312 |
if id_list == []: |
|---|
| 313 |
return {} |
|---|
| 314 |
qs = self._clone() |
|---|
| 315 |
qs._where.append("%s.%s IN (%s)" % (backend.quote_name(self.model._meta.db_table), backend.quote_name(self.model._meta.pk.column), ",".join(['%s'] * len(id_list)))) |
|---|
| 316 |
qs._params.extend(id_list) |
|---|
| 317 |
return dict([(obj._get_pk_val(), obj) for obj in qs.iterator()]) |
|---|
| 318 |
|
|---|
| 319 |
def delete(self): |
|---|
| 320 |
""" |
|---|
| 321 |
Deletes the records in the current QuerySet. |
|---|
| 322 |
""" |
|---|
| 323 |
assert self._limit is None and self._offset is None, \ |
|---|
| 324 |
"Cannot use 'limit' or 'offset' with delete." |
|---|
| 325 |
|
|---|
| 326 |
del_query = self._clone() |
|---|
| 327 |
|
|---|
| 328 |
# disable non-supported fields |
|---|
| 329 |
del_query._select_related = False |
|---|
| 330 |
del_query._order_by = [] |
|---|
| 331 |
|
|---|
| 332 |
# Delete objects in chunks to prevent an the list of |
|---|
| 333 |
# related objects from becoming too long |
|---|
| 334 |
more_objects = True |
|---|
| 335 |
while more_objects: |
|---|
| 336 |
# Collect all the objects to be deleted in this chunk, and all the objects |
|---|
| 337 |
# that are related to the objects that are to be deleted |
|---|
| 338 |
seen_objs = SortedDict() |
|---|
| 339 |
more_objects = False |
|---|
| 340 |
for object in del_query[0:GET_ITERATOR_CHUNK_SIZE]: |
|---|
| 341 |
more_objects = True |
|---|
| 342 |
object._collect_sub_objects(seen_objs) |
|---|
| 343 |
|
|---|
| 344 |
# If one or more objects were found, delete them. |
|---|
| 345 |
# Otherwise, stop looping. |
|---|
| 346 |
if more_objects: |
|---|
| 347 |
delete_objects(seen_objs) |
|---|
| 348 |
|
|---|
| 349 |
# Clear the result cache, in case this QuerySet gets reused. |
|---|
| 350 |
self._result_cache = None |
|---|
| 351 |
delete.alters_data = True |
|---|
| 352 |
|
|---|
| 353 |
################################################## |
|---|
| 354 |
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS # |
|---|
| 355 |
################################################## |
|---|
| 356 |
|
|---|
| 357 |
def values(self, *fields): |
|---|
| 358 |
return self._clone(klass=ValuesQuerySet, _fields=fields) |
|---|
| 359 |
|
|---|
| 360 |
def dates(self, field_name, kind, order='ASC'): |
|---|
| 361 |
""" |
|---|
| 362 |
Returns a list of datetime objects representing all available dates |
|---|
| 363 |
for the given field_name, scoped to 'kind'. |
|---|
| 364 |
""" |
|---|
| 365 |
assert kind in ("month", "year", "day"), "'kind' must be one of 'year', 'month' or 'day'." |
|---|
| 366 |
assert order in ('ASC', 'DESC'), "'order' must be either 'ASC' or 'DESC'." |
|---|
| 367 |
# Let the FieldDoesNotExist exception propagate. |
|---|
| 368 |
field = self.model._meta.get_field(field_name, many_to_many=False) |
|---|
| 369 |
assert isinstance(field, DateField), "%r isn't a DateField." % field_name |
|---|
| 370 |
return self._clone(klass=DateQuerySet, _field=field, _kind=kind, _order=order) |
|---|
| 371 |
|
|---|
| 372 |
################################################################## |
|---|
| 373 |
# PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET # |
|---|
| 374 |
################################################################## |
|---|
| 375 |
|
|---|
| 376 |
def filter(self, *args, **kwargs): |
|---|
| 377 |
"Returns a new QuerySet instance with the args ANDed to the existing set." |
|---|
| 378 |
return self._filter_or_exclude(None, *args, **kwargs) |
|---|
| 379 |
|
|---|
| 380 |
def exclude(self, *args, **kwargs): |
|---|
| 381 |
"Returns a new QuerySet instance with NOT (args) ANDed to the existing set." |
|---|
| 382 |
return self._filter_or_exclude(QNot, *args, **kwargs) |
|---|
| 383 |
|
|---|
| 384 |
def _filter_or_exclude(self, mapper, *args, **kwargs): |
|---|
| 385 |
# mapper is a callable used to transform Q objects, |
|---|
| 386 |
# or None for identity transform |
|---|
| 387 |
if mapper is None: |
|---|
| 388 |
mapper = lambda x: x |
|---|
| 389 |
if len(args) > 0 or len(kwargs) > 0: |
|---|
| 390 |
assert self._limit is None and self._offset is None, \ |
|---|
| 391 |
"Cannot filter a query once a slice has been taken." |
|---|
| 392 |
|
|---|
| 393 |
clone = self._clone() |
|---|
| 394 |
if len(kwargs) > 0: |
|---|
| 395 |
clone._filters = clone._filters & mapper(Q(**kwargs)) |
|---|
| 396 |
if len(args) > 0: |
|---|
| 397 |
clone._filters = clone._filters & reduce(operator.and_, map(mapper, args)) |
|---|
| 398 |
return clone |
|---|
| 399 |
|
|---|
| 400 |
def complex_filter(self, filter_obj): |
|---|
| 401 |
"""Returns a new QuerySet instance with filter_obj added to the filters. |
|---|
| 402 |
filter_obj can be a Q object (has 'get_sql' method) or a dictionary of |
|---|
| 403 |
keyword lookup arguments.""" |
|---|
| 404 |
# This exists to support framework features such as 'limit_choices_to', |
|---|
| 405 |
# and usually it will be more natural to use other methods. |
|---|
| 406 |
if hasattr(filter_obj, 'get_sql'): |
|---|
| 407 |
return self._filter_or_exclude(None, filter_obj) |
|---|
| 408 |
else: |
|---|
| 409 |
return self._filter_or_exclude(None, **filter_obj) |
|---|
| 410 |
|
|---|
| 411 |
def select_related(self, true_or_false=True, depth=0): |
|---|
| 412 |
"Returns a new QuerySet instance with '_select_related' modified." |
|---|
| 413 |
return self._clone(_select_related=true_or_false, _max_related_depth=depth) |
|---|
| 414 |
|
|---|
| 415 |
def order_by(self, *field_names): |
|---|
| 416 |
"Returns a new QuerySet instance with the ordering changed." |
|---|
| 417 |
assert self._limit is None and self._offset is None, \ |
|---|
| 418 |
"Cannot reorder a query once a slice has been taken." |
|---|
| 419 |
return self._clone(_order_by=field_names) |
|---|
| 420 |
|
|---|
| 421 |
def distinct(self, true_or_false=True): |
|---|
| 422 |
"Returns a new QuerySet instance with '_distinct' modified." |
|---|
| 423 |
return self._clone(_distinct=true_or_false) |
|---|
| 424 |
|
|---|
| 425 |
def extra(self, select=None, where=None, params=None, tables=None): |
|---|
| 426 |
assert self._limit is None and self._offset is None, \ |
|---|
| 427 |
"Cannot change a query once a slice has been taken" |
|---|
| 428 |
clone = self._clone() |
|---|
| 429 |
if select: clone._select.update(select) |
|---|
| 430 |
if where: clone._where.extend(where) |
|---|
| 431 |
if params: clone._params.extend(params) |
|---|
| 432 |
if tables: clone._tables.extend(tables) |
|---|
| 433 |
return clone |
|---|
| 434 |
|
|---|
| 435 |
################### |
|---|
| 436 |
# PRIVATE METHODS # |
|---|
| 437 |
################### |
|---|
| 438 |
|
|---|
| 439 |
def _clone(self, klass=None, **kwargs): |
|---|
| 440 |
if klass is None: |
|---|
| 441 |
klass = self.__class__ |
|---|
| 442 |
c = klass() |
|---|
| 443 |
c.model = self.model |
|---|
| 444 |
c._filters = self._filters |
|---|
| 445 |
c._order_by = self._order_by |
|---|
| 446 |
c._select_related = self._select_related |
|---|
| 447 |
c._max_related_depth = self._max_related_depth |
|---|
| 448 |
c._distinct = self._distinct |
|---|
| 449 |
c._select = self._select.copy() |
|---|
| 450 |
c._where = self._where[:] |
|---|
| 451 |
c._params = self._params[:] |
|---|
| 452 |
c._tables = self._tables[:] |
|---|
| 453 |
c._offset = self._offset |
|---|
| 454 |
c._limit = self._limit |
|---|
| 455 |
c.__dict__.update(kwargs) |
|---|
| 456 |
return c |
|---|
| 457 |
|
|---|
| 458 |
def _combine(self, other): |
|---|
| 459 |
assert self._limit is None and self._offset is None \ |
|---|
| 460 |
and other._limit is None and other._offset is None, \ |
|---|
| 461 |
"Cannot combine queries once a slice has been taken." |
|---|
| 462 |
assert self._distinct == other._distinct, \ |
|---|
| 463 |
"Cannot combine a unique query with a non-unique query" |
|---|
| 464 |
# use 'other's order by |
|---|
| 465 |
# (so that A.filter(args1) & A.filter(args2) does the same as |
|---|
| 466 |
# A.filter(args1).filter(args2) |
|---|
| 467 |
combined = other._clone() |
|---|
| 468 |
if self._select: combined._select.update(self._select) |
|---|
| 469 |
if self._where: combined._where.extend(self._where) |
|---|
| 470 |
if self._params: combined._params.extend(self._params) |
|---|
| 471 |
if self._tables: combined._tables.extend(self._tables) |
|---|
| 472 |
# If 'self' is ordered and 'other' isn't, propagate 'self's ordering |
|---|
| 473 |
if (self._order_by is not None and len(self._order_by) > 0) and \ |
|---|
| 474 |
(combined._order_by is None or len(combined._order_by) == 0): |
|---|
| 475 |
combined._order_by = self._order_by |
|---|
| 476 |
return combined |
|---|
| 477 |
|
|---|
| 478 |
def _get_data(self): |
|---|
| 479 |
if self._result_cache is None: |
|---|
| 480 |
self._result_cache = list(self.iterator()) |
|---|
| 481 |
return self._result_cache |
|---|
| 482 |
|
|---|
| 483 |
def _get_sql_clause(self): |
|---|
| 484 |
opts = self.model._meta |
|---|
| 485 |
|
|---|
| 486 |
# Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z. |
|---|
| 487 |
select = ["%s.%s" % (backend.quote_name(opts.db_table), backend.quote_name(f.column)) for f in opts |
|---|