Django

Code

root/django/branches/unicode/django/db/models/query.py

Revision 5580, 47.2 kB (checked in by mtredinnick, 1 year ago)

unicode: Merged changes from trunk up to [5579].

  • Property svn:eol-style set to native
Line 
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