Django

Code

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

Revision 5580, 47.2 kB (checked in by mtredinnick, 2 years 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.fields]
488         tables = [quote_only_if_word(t) for t in self._tables]
489         joins = SortedDict()
490         where = self._where[:]
491         params = self._params[:]
492
493         # Convert self._filters into SQL.
494         joins2, where2, params2 = self._filters.get_sql(opts)
495         joins.update(joins2)
496         where.extend(where2)
497         params.extend(params2)
498
499         # Add additional tables and WHERE clauses based on select_related.
500         if self._select_related:
501             fill_table_cache(opts, select, tables, where,
502                              old_prefix=opts.db_table,
503                              cache_tables_seen=[opts.db_table],
504                              max_depth=self._max_related_depth)
505
506         # Add any additional SELECTs.
507         if self._select:
508             select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()])
509
510         # Start composing the body of the SQL statement.
511         sql = [" FROM", backend.quote_name(opts.db_table)]
512
513         # Compose the join dictionary into SQL describing the joins.
514         if joins:
515             sql.append(" ".join(["%s %s AS %s ON %s" % (join_type, table, alias, condition)
516                             for (alias, (table, join_type, condition)) in joins.items()]))
517
518         # Compose the tables clause into SQL.
519         if tables:
520             sql.append(", " + ", ".join(tables))
521
522         # Compose the where clause into SQL.
523         if where:
524             sql.append(where and "WHERE " + " AND ".join(where))
525
526         # ORDER BY clause
527         order_by = []
528         if self._order_by is not None:
529             ordering_to_use = self._order_by
530         else:
531             ordering_to_use = opts.ordering
532         for f in handle_legacy_orderlist(ordering_to_use):
533             if f == '?': # Special case.
534                 order_by.append(backend.get_random_function_sql())
535             else:
536                 if f.startswith('-'):
537                     col_name = f[1:]
538                     order = "DESC"
539                 else:
540                     col_name = f
541                     order = "ASC"
542                 if "." in col_name:
543                     table_prefix, col_name = col_name.split('.', 1)
544                     table_prefix = backend.quote_name(table_prefix) + '.'
545                 else:
546                     # Use the database table as a column prefix if it wasn't given,
547                     # and if the requested column isn't a custom SELECT.
548                     if "." not in col_name and col_name not in (self._select or ()):
549                         table_prefix = backend.quote_name(opts.db_table) + '.'
550                     else:
551                         table_prefix = ''
552                 order_by.append('%s%s %s' % (table_prefix, backend.quote_name(orderfield2column(col_name, opts)), order))
553         if order_by:
554             sql.append("ORDER BY " + ", ".join(order_by))
555
556         # LIMIT and OFFSET clauses
557         if self._limit is not None:
558             sql.append("%s " % backend.get_limit_offset_sql(self._limit, self._offset))
559         else:
560             assert self._offset is None, "'offset' is not allowed without 'limit'"
561
562         return select, " ".join(sql), params
563
564 # Use the backend's QuerySet class if it defines one, otherwise use _QuerySet.
565 if hasattr(backend, 'get_query_set_class'):
566     QuerySet = backend.get_query_set_class(_QuerySet)
567 else:
568     QuerySet = _QuerySet
569
570 class ValuesQuerySet(QuerySet):
571     def __init__(self, *args, **kwargs):
572         super(ValuesQuerySet, self).__init__(*args, **kwargs)
573         # select_related isn't supported in values().
574         self._select_related = False
575
576     def iterator(self):
577         try:
578             select, sql, params = self._get_sql_clause()
579         except EmptyResultSet:
580             raise StopIteration
581
582         # self._fields is a list of field names to fetch.
583         if self._fields:
584             if not self._select:
585                 fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields]
586             else:
587                 fields = []
588                 for f in self._fields:
589                     if f in [field.name for field in self.model._meta.fields]:
590                         fields.append(self.model._meta.get_field(f, many_to_many=False))
591                     elif not self._select.has_key( f ):
592                         raise FieldDoesNotExist, '%s has no field named %r' % ( self.model._meta.object_name, f )
593
594             field_names = self._fields
595         else: # Default to all fields.
596             fields = self.model._meta.fields
597             field_names = [f.attname for f in fields]
598
599         columns = [f.column for f in fields]
600         select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
601         # Add any additional SELECTs.
602         if self._select:
603             select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()])
604
605         cursor = connection.cursor()
606         cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
607
608         has_resolve_columns = hasattr(self, 'resolve_columns')
609         while 1:
610             rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
611             if not rows:
612                 raise StopIteration
613             for row in rows:
614                 if has_resolve_columns:
615                     row = self.resolve_columns(row, fields)
616                 yield dict(zip(field_names, row))
617
618     def _clone(self, klass=None, **kwargs):
619         c = super(ValuesQuerySet, self)._clone(klass, **kwargs)
620         c._fields = self._fields[:]
621         return c
622
623 class DateQuerySet(QuerySet):
624     def iterator(self):
625         from django.db.backends.util import typecast_timestamp
626         from django.db.models.fields import DateTimeField
627         self._order_by = () # Clear this because it'll mess things up otherwise.
628         if self._field.null:
629             self._where.append('%s.%s IS NOT NULL' % \
630                 (backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column)))
631         try:
632             select, sql, params = self._get_sql_clause()
633         except EmptyResultSet:
634             raise StopIteration
635
636         table_name = backend.quote_name(self.model._meta.db_table)
637         field_name = backend.quote_name(self._field.column)
638
639         if backend.allows_group_by_ordinal:
640             group_by = '1'
641         else:
642             group_by = backend.get_date_trunc_sql(self._kind,
643                                                   '%s.%s' % (table_name, field_name))
644
645         sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \
646             (backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table),
647             backend.quote_name(self._field.column))), sql, group_by, self._order)
648         cursor = connection.cursor()
649         cursor.execute(sql, params)
650
651         has_resolve_columns = hasattr(self, 'resolve_columns')
652         needs_datetime_string_cast = backend.needs_datetime_string_cast
653         dates = []
654         # It would be better to use self._field here instead of DateTimeField(),
655         # but in Oracle that will result in a list of datetime.date instead of
656         # datetime.datetime.
657         fields = [DateTimeField()]
658         while 1:
659             rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
660             if not rows:
661                 return dates
662             for row in rows:
663                 date = row[0]
664                 if has_resolve_columns:
665                     date = self.resolve_columns([date], fields)[0]
666                 elif needs_datetime_string_cast:
667                     date = typecast_timestamp(str(date))
668                 dates.append(date)
669
670     def _clone(self, klass=None, **kwargs):
671         c = super(DateQuerySet, self)._clone(klass, **kwargs)
672         c._field = self._field
673         c._kind = self._kind
674         c._order = self._order
675         return c
676
677 class EmptyQuerySet(QuerySet):
678     def __init__(self, model=None):
679         super(EmptyQuerySet, self).__init__(model)
680         self._result_cache = []
681
682     def count(self):
683         return 0
684
685     def delete(self):
686         pass
687
688     def _clone(self, klass=None, **kwargs):
689         c = super(EmptyQuerySet, self)._clone(klass, **kwargs)
690         c._result_cache = []
691         return c
692
693     def _get_sql_clause(self):
694         raise EmptyResultSet
695
696 class QOperator(object):
697     "Base class for QAnd and QOr"
698     def __init__(self, *args):
699         self.args = args
700
701     def get_sql(self, opts):
702         joins, where, params = SortedDict(), [], []
703         for val in self.args:
704             try:
705                 joins2, where2, params2 = val.get_sql(opts)
706                 joins.update(joins2)
707                 where.extend(where2)
708                 params.extend(params2)
709             except EmptyResultSet:
710                 if not isinstance(self, QOr):
711                     raise EmptyResultSet
712         if where:
713             return joins, ['(%s)' % self.operator.join(where)], params
714         return joins, [], params
715
716 class QAnd(QOperator):
717     "Encapsulates a combined query that uses 'AND'."
718     operator = ' AND '
719     def __or__(self, other):
720         return QOr(self, other)
721
722     def __and__(self, other):
723         if isinstance(other, QAnd):
724             return QAnd(*(self.args+other.args))
725         elif isinstance(other, (Q, QOr)):
726             return QAnd(*(self.args+(other,)))
727         else:
728             raise TypeError, other
729
730 class QOr(QOperator):
731     "Encapsulates a combined query that uses 'OR'."
732     operator = ' OR '
733     def __and__(self, other):
734         return QAnd(self, other)
735
736     def __or__(self, other):
737         if isinstance(other, QOr):
738             return QOr(*(self.args+other.args))
739         elif isinstance(other, (Q, QAnd)):
740             return QOr(*(self.args+(other,)))
741         else:
742             raise TypeError, other
743
744 class Q(object):
745     "Encapsulates queries as objects that can be combined logically."
746     def __init__(self, **kwargs):
747         self.kwargs = kwargs
748
749     def __and__(self, other):
750         return QAnd(self, other)
751
752     def __or__(self, other):
753         return QOr(self, other)
754
755     def get_sql(self, opts):
756         return parse_lookup(self.kwargs.items(), opts)
757
758 class QNot(Q):
759     "Encapsulates NOT (...) queries as objects"
760     def __init__(self, q):
761         "Creates a negation of the q object passed in."
762         self.q = q
763
764     def get_sql(self, opts):
765         try:
766             joins, where, params = self.q.get_sql(opts)
767             where2 = ['(NOT (%s))' % " AND ".join(where)]
768         except EmptyResultSet:
769             return SortedDict(), [], []
770         return joins, where2, params
771
772 def get_where_clause(lookup_type, table_prefix, field_name, value):
773     if table_prefix.endswith('.'):
774         table_prefix = backend.quote_name(table_prefix[:-1])+'.'
775     field_name = backend.quote_name(field_name)
776     if type(value) == datetime.datetime and backend.get_datetime_cast_sql():
777         cast_sql = backend.get_datetime_cast_sql()
778     else:
779         cast_sql = '%s'
780     if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops:
781         format = 'UPPER(%s%s) %s'
782     else:
783         format = '%s%s %s'
784     try:
785         return format % (table_prefix, field_name,
786                          backend.OPERATOR_MAPPING[lookup_type] % cast_sql)
787     except KeyError:
788         pass
789     if lookup_type == 'in':
790         in_string = ','.join(['%s' for id in value])
791         if in_string:
792             return '%s%s IN (%s)' % (table_prefix, field_name, in_string)
793         else:
794             raise EmptyResultSet
795     elif lookup_type in ('range', 'year'):
796         return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name)
797     elif lookup_type in ('month', 'day'):
798         return "%s = %%s" % backend.get_date_extract_sql(lookup_type, table_prefix + field_name)
799     elif lookup_type == 'isnull':
800         return "%s%s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or ''))
801     elif lookup_type == 'search':
802         return backend.get_fulltext_search_sql(table_prefix + field_name)
803     elif lookup_type in ('regex', 'iregex'):
804         if settings.DATABASE_ENGINE == 'oracle':
805             if lookup_type == 'regex':
806                 match_option = 'c'
807             else:
808                 match_option = 'i'
809             return "REGEXP_LIKE(%s%s, %s, '%s')" % (table_prefix, field_name, cast_sql, match_option)
810         else:
811             raise NotImplementedError
812     raise TypeError, "Got invalid lookup_type: %s" % repr(lookup_type)
813
814 def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0):
815     """Helper function that recursively returns an object with cache filled"""
816
817     # If we've got a max_depth set and we've exceeded that depth, bail now.
818     if max_depth and cur_depth > max_depth:
819         return None
820
821     index_end = index_start + len(klass._meta.fields)
822     obj = klass(*row[index_start:index_end])
823     for f in klass._meta.fields:
824         if f.rel and not f.null:
825             cached_row = get_cached_row(f.rel.to, row, index_end, max_depth, cur_depth+1)
826             if cached_row:
827                 rel_obj, index_end = cached_row
828                 setattr(obj, f.get_cache_name(), rel_obj)
829     return obj, index_end
830
831 def fill_table_cache(opts, select, tables, where, old_prefix, cache_tables_seen, max_depth=0, cur_depth=0):
832     """
833     Helper function that recursively populates the select, tables and where (in
834     place) for select_related queries.
835     """
836
837     # If we've got a max_depth set and we've exceeded that depth, bail now.
838     if max_depth and cur_depth > max_depth:
839         return None
840
841     qn = backend.quote_name
842     for f in opts.fields:
843         if f.rel and not f.null:
844             db_table = f.rel.to._meta.db_table
845             if db_table not in cache_tables_seen:
846                 tables.append(qn(db_table))
847             else: # The table was already seen, so give it a table alias.
848                 new_prefix = '%s%s' % (db_table, len(cache_tables_seen))
849                 tables.append('%s %s' % (qn(db_table), qn(new_prefix)))
850                 db_table = new_prefix
851             cache_tables_seen.append(db_table)
852             where.append('%s.%s = %s.%s' % \
853                 (qn(old_prefix), qn(f.column), qn(db_table), qn(f.rel.get_related_field().column)))
854             select.extend(['%s.%s' % (qn(db_table), qn(f2.column)) for f2 in f.rel.to._meta.fields])
855             fill_table_cache(f.rel.to._meta, select, tables, where, db_table, cache_tables_seen, max_depth, cur_depth+1)
856
857 def parse_lookup(kwarg_items, opts):
858     # Helper function that handles converting API kwargs
859     # (e.g. "name__exact": "tom") to SQL.
860     # Returns a tuple of (joins, where, params).
861
862     # 'joins' is a sorted dictionary describing the tables that must be joined
863     # to complete the query. The dictionary is sorted because creation order
864     # is significant; it is a dictionary to ensure uniqueness of alias names.
865     #
866     # Each key-value pair follows the form
867     #   alias: (table, join_type, condition)
868     # where
869     #   alias is the AS alias for the joined table
870     #   table is the actual table name to be joined
871     #   join_type is the type of join (INNER JOIN, LEFT OUTER JOIN, etc)
872     #   condition is the where-like statement over which narrows the join.
873     #   alias will be derived from the lookup list name.
874     #
875     # At present, this method only every returns INNER JOINs; the option is
876     # there for others to implement custom Q()s, etc that return other join
877     # types.
878     joins, where, params = SortedDict(), [], []
879
880     for kwarg, value in kwarg_items:
881         path = kwarg.split(LOOKUP_SEPARATOR)
882         # Extract the last elements of the kwarg.
883         # The very-last is the lookup_type (equals, like, etc).
884         # The second-last is the table column on which the lookup_type is
885         # to be performed. If this name is 'pk', it will be substituted with
886         # the name of the primary key.
887         # If there is only one part, or the last part is not a query
888         # term, assume that the query is an __exact
889         lookup_type = path.pop()
890         if lookup_type == 'pk':
891             lookup_type = 'exact'
892             path.append(None)
893         elif len(path) == 0 or lookup_type not in QUERY_TERMS:
894             path.append(lookup_type)
895             lookup_type = 'exact'
896
897         if len(path) < 1:
898             raise TypeError, "Cannot parse keyword query %r" % kwarg
899
900         if value is None:
901             # Interpret '__exact=None' as the sql '= NULL'; otherwise, reject
902             # all uses of None as a query value.
903             if lookup_type != 'exact':
904                 raise ValueError, "Cannot use None as a query value"
905         elif callable(value):
906             value = value()
907
908         joins2, where2, params2 = lookup_inner(path, lookup_type, value, opts, opts.db_table, None)
909         joins.update(joins2)
910         where.extend(where2)
911         params.extend(params2)
912     return joins, where, params
913
914 class FieldFound(Exception):
915     "Exception used to short circuit field-finding operations."
916     pass
917
918 def find_field(name, field_list, related_query):
919     """
920     Finds a field with a specific name in a list of field instances.
921     Returns None if there are no matches, or several matches.
922     """
923     if related_query:
924         matches = [f for f in field_list if f.field.related_query_name() == name]
925     else:
926         matches = [f for f in field_list if f.name == name]
927     if len(matches) != 1:
928         return None
929     return matches[0]
930
931 def field_choices(field_list, related_query):
932     if related_query:
933         choices = [f.field.related_query_name() for f in field_list]
934     else:
935         choices = [f.name for f in field_list]
936     return choices
937
938 def lookup_inner(path, lookup_type, value, opts, table, column):
939     qn = backend.quote_name
940     joins, where, params = SortedDict(), [], []
941     current_opts = opts
942     current_table = table
943     current_column = column
944     intermediate_table = None
945     join_required = False
946
947     name = path.pop(0)
948     # Has the primary key been requested? If so, expand it out
949     # to be the name of the current class' primary key
950     if name is None or name == 'pk':
951         name = current_opts.pk.name
952
953     # Try to find the name in the fields associated with the current class
954     try:
955         # Does the name belong to a defined many-to-many field?
956         field = find_field(name, current_opts.many_to_many, False)
957         if field:
958             new_table = current_table + '__' + name
959             new_opts = field.rel.to._meta
960             new_column = new_opts.pk.column
961
962             # Need to create an intermediate table join over the m2m table
963             # This process hijacks current_table/column to point to the
964             # intermediate table.
965             current_table = "m2m_" + new_table
966             intermediate_table = field.m2m_db_table()
967             join_column = field.m2m_reverse_name()
968             intermediate_column = field.m2m_column_name()
969
970             raise FieldFound
971
972         # Does the name belong to a reverse defined many-to-many field?
973         field = find_field(name, current_opts.get_all_related_many_to_many_objects(), True)
974         if field:
975             new_table = current_table + '__' + name
976             new_opts = field.opts
977             new_column = new_opts.pk.column
978
979             # Need to create an intermediate table join over the m2m table.
980             # This process hijacks current_table/column to point to the
981             # intermediate table.
982             current_table = "m2m_" + new_table
983             intermediate_table = field.field.m2m_db_table()
984             join_column = field.field.m2m_column_name()
985             intermediate_column = field.field.m2m_reverse_name()
986
987             raise FieldFound
988
989         # Does the name belong to a one-to-many field?
990         field = find_field(name, current_opts.get_all_related_objects(), True)
991         if field:
992             new_table = table + '__' + name
993             new_opts = field.opts
994             new_column = field.field.column
995             join_column = opts.pk.column
996
997             # 1-N fields MUST be joined, regardless of any other conditions.
998             join_required = True
999
1000             raise FieldFound
1001
1002         # Does the name belong to a one-to-one, many-to-one, or regular field?
1003         field = find_field(name, current_opts.fields, False)
1004         if field:
1005             if field.rel: # One-to-One/Many-to-one field
1006                 new_table = current_table + '__' + name
1007                 new_opts = field.rel.to._meta
1008                 new_column = new_opts.pk.column
1009                 join_column = field.column
1010                 raise FieldFound
1011             elif path:
1012                 # For regular fields, if there are still items on the path,
1013                 # an error has been made. We munge "name" so that the error
1014                 # properly identifies the cause of the problem.
1015                 name += LOOKUP_SEPARATOR + path[0]
1016             else:
1017                 raise FieldFound
1018
1019     except FieldFound: # Match found, loop has been shortcut.
1020         pass
1021     else: # No match found.
1022         choices = field_choices(current_opts.many_to_many, False) + \
1023             field_choices(current_opts.get_all_related_many_to_many_objects(), True) + \
1024             field_choices(current_opts.get_all_related_objects(), True) + \
1025             field_choices(current_opts.fields, False)
1026         raise TypeError, "Cannot resolve keyword '%s' into field. Choices are: %s" % (name, ", ".join(choices))
1027
1028     # Check whether an intermediate join is required between current_table
1029     # and new_table.
1030     if intermediate_table:
1031         joins[qn(current_table)] = (
1032             qn(intermediate_table), "LEFT OUTER JOIN",
1033             "%s.%s = %s.%s" % (qn(table), qn(current_opts.pk.column), qn(current_table), qn(intermediate_column))
1034         )
1035
1036     if path:
1037         # There are elements left in the path. More joins are required.
1038         if len(path) == 1 and path[0] in (new_opts.pk.name, None) \
1039             and lookup_type in ('exact', 'isnull') and not join_required:
1040             # If the next and final name query is for a primary key,
1041             # and the search is for isnull/exact, then the current
1042             # (for N-1) or intermediate (for N-N) table can be used
1043             # for the search. No need to join an extra table just
1044             # to check the primary key.
1045             new_table = current_table
1046         else:
1047             # There are 1 or more name queries pending, and we have ruled out
1048             # any shortcuts; therefore, a join is required.
1049             joins[qn(new_table)] = (
1050                 qn(new_opts.db_table), "INNER JOIN",
1051                 "%s.%s = %s.%s" % (qn(current_table), qn(join_column), qn(new_table), qn(new_column))
1052             )
1053             # If we have made the join, we don't need to tell subsequent
1054             # recursive calls about the column name we joined on.
1055             join_column = None
1056
1057         # There are name queries remaining. Recurse deeper.
1058         joins2, where2, params2 = lookup_inner(path, lookup_type, value, new_opts, new_table, join_column)
1059
1060         joins.update(joins2)
1061         where.extend(where2)
1062         params.extend(params2)
1063     else:
1064         # No elements left in path. Current element is the element on which
1065         # the search is being performed.
1066
1067         if join_required:
1068             # Last query term is a RelatedObject
1069             if field.field.rel.multiple:
1070                 # RelatedObject is from a 1-N relation.
1071                 # Join is required; query operates on joined table.
1072                 column = new_opts.pk.name
1073                 joins[qn(new_table)] = (
1074                     qn(new_opts.db_table), "INNER JOIN",
1075                     "%s.%s = %s.%s" % (qn(current_table), qn(join_column), qn(new_table), qn(new_column))
1076                 )
1077                 current_table = new_table
1078             else:
1079                 # RelatedObject is from a 1-1 relation,
1080                 # No need to join; get the pk value from the related object,
1081                 # and compare using that.
1082                 column = current_opts.pk.name
1083         elif intermediate_table:
1084             # Last query term is a related object from an N-N relation.
1085             # Join from intermediate table is sufficient.
1086             column = join_column
1087         elif name == current_opts.pk.name and lookup_type in ('exact', 'isnull') and current_column:
1088             # Last query term is for a primary key. If previous iterations
1089             # introduced a current/intermediate table that can be used to
1090             # optimize the query, then use that table and column name.
1091             column = current_column
1092         else:
1093             # Last query term was a normal field.
1094             column = field.column
1095
1096         where.append(get_where_clause(lookup_type, current_table + '.', column, value))
1097         params.extend(field.get_db_prep_lookup(lookup_type, value))
1098
1099     return joins, where, params
1100
1101 def delete_objects(seen_objs):
1102     "Iterate through a list of seen classes, and remove any instances that are referred to"
1103     qn = backend.quote_name
1104     ordered_classes = seen_objs.keys()
1105     ordered_classes.reverse()
1106
1107     cursor = connection.cursor()
1108
1109     for cls in ordered_classes:
1110         seen_objs[cls] = seen_objs[cls].items()
1111         seen_objs[cls].sort()
1112
1113         # Pre notify all instances to be deleted
1114         for pk_val, instance in seen_objs[cls]:
1115             dispatcher.send(signal=signals.pre_delete, sender=cls, instance=instance)
1116
1117         pk_list = [pk for pk,instance in seen_objs[cls]]
1118         for related in cls._meta.get_all_related_many_to_many_objects():
1119             if not isinstance(related.field, generic.GenericRelation):
1120                 for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
1121                     cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
1122                         (qn(related.field.m2m_db_table()),
1123                             qn(related.field.m2m_reverse_name()),
1124                             ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
1125                         pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
1126         for f in cls._meta.many_to_many:
1127             if isinstance(f, generic.GenericRelation):
1128                 from django.contrib.contenttypes.models import ContentType
1129                 query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
1130                 args_extra = [ContentType.objects.get_for_model(cls).id]
1131             else:
1132                 query_extra = ''
1133                 args_extra = []
1134             for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
1135                 cursor.execute(("DELETE FROM %s WHERE %s IN (%s)" % \
1136                     (qn(f.m2m_db_table()), qn(f.m2m_column_name()),
1137                     ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]]))) + query_extra,
1138                     pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + args_extra)
1139         for field in cls._meta.fields:
1140             if field.rel and field.null and field.rel.to in seen_objs:
1141                 for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
1142                     cursor.execute("UPDATE %s SET %s=NULL WHERE %s IN (%s)" % \
1143                         (qn(cls._meta.db_table), qn(field.column), qn(cls._meta.pk.column),
1144                             ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
1145                         pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
1146
1147     # Now delete the actual data
1148     for cls in ordered_classes:
1149         seen_objs[cls].reverse()
1150         pk_list = [pk for pk,instance in seen_objs[cls]]
1151         for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
1152             cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
1153                 (qn(cls._meta.db_table), qn(cls._meta.pk.column),
1154                 ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
1155                 pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
1156
1157         # Last cleanup; set NULLs where there once was a reference to the object,
1158         # NULL the primary key of the found objects, and perform post-notification.
1159         for pk_val, instance in seen_objs[cls]:
1160             for field in cls._meta.fields:
1161                 if field.rel and field.null and field.rel.to in seen_objs:
1162                     setattr(instance, field.attname, None)
1163
1164             setattr(instance, cls._meta.pk.attname, None)
1165             dispatcher.send(signal=signals.post_delete, sender=cls, instance=instance)
1166
1167     transaction.commit_unless_managed()
Note: See TracBrowser for help on using the browser.