Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
"""
|
|
|
|
Query subclasses which provide extra functionality beyond simple data retrieval.
|
|
|
|
"""
|
|
|
|
|
|
|
|
from django.core.exceptions import FieldError
|
|
|
|
from django.db.models.sql.constants import *
|
2008-06-15 12:49:29 +08:00
|
|
|
from django.db.models.sql.datastructures import Date
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
from django.db.models.sql.query import Query
|
|
|
|
from django.db.models.sql.where import AND
|
|
|
|
|
|
|
|
__all__ = ['DeleteQuery', 'UpdateQuery', 'InsertQuery', 'DateQuery',
|
|
|
|
'CountQuery']
|
|
|
|
|
|
|
|
class DeleteQuery(Query):
|
|
|
|
"""
|
|
|
|
Delete queries are done through this class, since they are more constrained
|
|
|
|
than general queries.
|
|
|
|
"""
|
|
|
|
def as_sql(self):
|
|
|
|
"""
|
|
|
|
Creates the SQL for this query. Returns the SQL string and list of
|
|
|
|
parameters.
|
|
|
|
"""
|
|
|
|
assert len(self.tables) == 1, \
|
|
|
|
"Can only delete from one table at a time."
|
|
|
|
result = ['DELETE FROM %s' % self.quote_name_unless_alias(self.tables[0])]
|
|
|
|
where, params = self.where.as_sql()
|
|
|
|
result.append('WHERE %s' % where)
|
|
|
|
return ' '.join(result), tuple(params)
|
|
|
|
|
|
|
|
def do_query(self, table, where):
|
|
|
|
self.tables = [table]
|
|
|
|
self.where = where
|
|
|
|
self.execute_sql(None)
|
|
|
|
|
|
|
|
def delete_batch_related(self, pk_list):
|
|
|
|
"""
|
|
|
|
Set up and execute delete queries for all the objects related to the
|
|
|
|
primary key values in pk_list. To delete the objects themselves, use
|
|
|
|
the delete_batch() method.
|
|
|
|
|
|
|
|
More than one physical query may be executed if there are a
|
|
|
|
lot of values in pk_list.
|
|
|
|
"""
|
2008-06-15 12:49:29 +08:00
|
|
|
from django.contrib.contenttypes import generic
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
cls = self.model
|
|
|
|
for related in cls._meta.get_all_related_many_to_many_objects():
|
|
|
|
if not isinstance(related.field, generic.GenericRelation):
|
|
|
|
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
|
|
|
where = self.where_class()
|
|
|
|
where.add((None, related.field.m2m_reverse_name(),
|
|
|
|
related.field, 'in',
|
|
|
|
pk_list[offset : offset+GET_ITERATOR_CHUNK_SIZE]),
|
|
|
|
AND)
|
|
|
|
self.do_query(related.field.m2m_db_table(), where)
|
|
|
|
|
|
|
|
for f in cls._meta.many_to_many:
|
|
|
|
w1 = self.where_class()
|
|
|
|
if isinstance(f, generic.GenericRelation):
|
|
|
|
from django.contrib.contenttypes.models import ContentType
|
|
|
|
field = f.rel.to._meta.get_field(f.content_type_field_name)
|
|
|
|
w1.add((None, field.column, field, 'exact',
|
|
|
|
ContentType.objects.get_for_model(cls).id), AND)
|
|
|
|
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
|
|
|
where = self.where_class()
|
|
|
|
where.add((None, f.m2m_column_name(), f, 'in',
|
|
|
|
pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]),
|
|
|
|
AND)
|
|
|
|
if w1:
|
|
|
|
where.add(w1, AND)
|
|
|
|
self.do_query(f.m2m_db_table(), where)
|
|
|
|
|
|
|
|
def delete_batch(self, pk_list):
|
|
|
|
"""
|
|
|
|
Set up and execute delete queries for all the objects in pk_list. This
|
|
|
|
should be called after delete_batch_related(), if necessary.
|
|
|
|
|
|
|
|
More than one physical query may be executed if there are a
|
|
|
|
lot of values in pk_list.
|
|
|
|
"""
|
|
|
|
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
|
|
|
where = self.where_class()
|
|
|
|
field = self.model._meta.pk
|
|
|
|
where.add((None, field.column, field, 'in',
|
|
|
|
pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), AND)
|
|
|
|
self.do_query(self.model._meta.db_table, where)
|
|
|
|
|
|
|
|
class UpdateQuery(Query):
|
|
|
|
"""
|
|
|
|
Represents an "update" SQL query.
|
|
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super(UpdateQuery, self).__init__(*args, **kwargs)
|
|
|
|
self._setup_query()
|
|
|
|
|
|
|
|
def _setup_query(self):
|
|
|
|
"""
|
2008-04-28 08:58:42 +08:00
|
|
|
Runs on initialization and after cloning. Any attributes that would
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
normally be set in __init__ should go in here, instead, so that they
|
|
|
|
are also set up after a clone() call.
|
|
|
|
"""
|
|
|
|
self.values = []
|
|
|
|
self.related_ids = None
|
|
|
|
if not hasattr(self, 'related_updates'):
|
|
|
|
self.related_updates = {}
|
|
|
|
|
|
|
|
def clone(self, klass=None, **kwargs):
|
|
|
|
return super(UpdateQuery, self).clone(klass,
|
|
|
|
related_updates=self.related_updates.copy, **kwargs)
|
|
|
|
|
|
|
|
def execute_sql(self, result_type=None):
|
|
|
|
super(UpdateQuery, self).execute_sql(result_type)
|
|
|
|
for query in self.get_related_updates():
|
|
|
|
query.execute_sql(result_type)
|
|
|
|
|
|
|
|
def as_sql(self):
|
|
|
|
"""
|
|
|
|
Creates the SQL for this query. Returns the SQL string and list of
|
|
|
|
parameters.
|
|
|
|
"""
|
|
|
|
self.pre_sql_setup()
|
|
|
|
if not self.values:
|
|
|
|
return '', ()
|
|
|
|
table = self.tables[0]
|
|
|
|
qn = self.quote_name_unless_alias
|
|
|
|
result = ['UPDATE %s' % qn(table)]
|
|
|
|
result.append('SET')
|
|
|
|
values, update_params = [], []
|
|
|
|
for name, val, placeholder in self.values:
|
|
|
|
if val is not None:
|
|
|
|
values.append('%s = %s' % (qn(name), placeholder))
|
|
|
|
update_params.append(val)
|
|
|
|
else:
|
|
|
|
values.append('%s = NULL' % qn(name))
|
|
|
|
result.append(', '.join(values))
|
|
|
|
where, params = self.where.as_sql()
|
|
|
|
if where:
|
|
|
|
result.append('WHERE %s' % where)
|
|
|
|
return ' '.join(result), tuple(update_params + params)
|
|
|
|
|
|
|
|
def pre_sql_setup(self):
|
|
|
|
"""
|
|
|
|
If the update depends on results from other tables, we need to do some
|
|
|
|
munging of the "where" conditions to match the format required for
|
|
|
|
(portable) SQL updates. That is done here.
|
|
|
|
|
|
|
|
Further, if we are going to be running multiple updates, we pull out
|
|
|
|
the id values to update at this point so that they don't change as a
|
|
|
|
result of the progressive updates.
|
|
|
|
"""
|
|
|
|
self.select_related = False
|
|
|
|
self.clear_ordering(True)
|
|
|
|
super(UpdateQuery, self).pre_sql_setup()
|
|
|
|
count = self.count_active_tables()
|
|
|
|
if not self.related_updates and count == 1:
|
|
|
|
return
|
|
|
|
|
|
|
|
# We need to use a sub-select in the where clause to filter on things
|
|
|
|
# from other tables.
|
|
|
|
query = self.clone(klass=Query)
|
|
|
|
query.bump_prefix()
|
|
|
|
query.extra_select = {}
|
2008-04-28 19:51:52 +08:00
|
|
|
first_table = query.tables[0]
|
|
|
|
if query.alias_refcount[first_table] == 1:
|
|
|
|
# We can remove one table from the inner query.
|
|
|
|
query.unref_alias(first_table)
|
|
|
|
for i in xrange(1, len(query.tables)):
|
|
|
|
table = query.tables[i]
|
|
|
|
if query.alias_refcount[table]:
|
|
|
|
break
|
|
|
|
join_info = query.alias_map[table]
|
|
|
|
query.select = [(join_info[RHS_ALIAS], join_info[RHS_JOIN_COL])]
|
|
|
|
must_pre_select = False
|
|
|
|
else:
|
|
|
|
query.select = []
|
|
|
|
query.add_fields([query.model._meta.pk.name])
|
|
|
|
must_pre_select = not self.connection.features.update_can_self_select
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
|
|
|
|
# Now we adjust the current query: reset the where clause and get rid
|
|
|
|
# of all the tables we don't need (since they're in the sub-select).
|
|
|
|
self.where = self.where_class()
|
2008-04-28 19:51:52 +08:00
|
|
|
if self.related_updates or must_pre_select:
|
|
|
|
# Either we're using the idents in multiple update queries (so
|
|
|
|
# don't want them to change), or the db backend doesn't support
|
|
|
|
# selecting from the updating table (e.g. MySQL).
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
idents = []
|
|
|
|
for rows in query.execute_sql(MULTI):
|
|
|
|
idents.extend([r[0] for r in rows])
|
|
|
|
self.add_filter(('pk__in', idents))
|
|
|
|
self.related_ids = idents
|
|
|
|
else:
|
2008-04-28 19:51:52 +08:00
|
|
|
# The fast path. Filters and updates in one query.
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
self.add_filter(('pk__in', query))
|
|
|
|
for alias in self.tables[1:]:
|
|
|
|
self.alias_refcount[alias] = 0
|
|
|
|
|
|
|
|
def clear_related(self, related_field, pk_list):
|
|
|
|
"""
|
|
|
|
Set up and execute an update query that clears related entries for the
|
|
|
|
keys in pk_list.
|
|
|
|
|
|
|
|
This is used by the QuerySet.delete_objects() method.
|
|
|
|
"""
|
|
|
|
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
|
|
|
self.where = self.where_class()
|
|
|
|
f = self.model._meta.pk
|
|
|
|
self.where.add((None, f.column, f, 'in',
|
|
|
|
pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]),
|
|
|
|
AND)
|
|
|
|
self.values = [(related_field.column, None, '%s')]
|
|
|
|
self.execute_sql(None)
|
|
|
|
|
|
|
|
def add_update_values(self, values):
|
|
|
|
"""
|
|
|
|
Convert a dictionary of field name to value mappings into an update
|
|
|
|
query. This is the entry point for the public update() method on
|
|
|
|
querysets.
|
|
|
|
"""
|
|
|
|
values_seq = []
|
|
|
|
for name, val in values.iteritems():
|
|
|
|
field, model, direct, m2m = self.model._meta.get_field_by_name(name)
|
|
|
|
if not direct or m2m:
|
|
|
|
raise FieldError('Cannot update model field %r (only non-relations and foreign keys permitted).' % field)
|
|
|
|
values_seq.append((field, model, val))
|
|
|
|
return self.add_update_fields(values_seq)
|
|
|
|
|
|
|
|
def add_update_fields(self, values_seq):
|
|
|
|
"""
|
|
|
|
Turn a sequence of (field, model, value) triples into an update query.
|
|
|
|
Used by add_update_values() as well as the "fast" update path when
|
|
|
|
saving models.
|
|
|
|
"""
|
|
|
|
from django.db.models.base import Model
|
|
|
|
for field, model, val in values_seq:
|
|
|
|
# FIXME: Some sort of db_prep_* is probably more appropriate here.
|
|
|
|
if field.rel and isinstance(val, Model):
|
|
|
|
val = val.pk
|
|
|
|
|
|
|
|
# Getting the placeholder for the field.
|
|
|
|
if hasattr(field, 'get_placeholder'):
|
|
|
|
placeholder = field.get_placeholder(val)
|
|
|
|
else:
|
|
|
|
placeholder = '%s'
|
|
|
|
|
|
|
|
if model:
|
|
|
|
self.add_related_update(model, field.column, val, placeholder)
|
|
|
|
else:
|
|
|
|
self.values.append((field.column, val, placeholder))
|
|
|
|
|
|
|
|
def add_related_update(self, model, column, value, placeholder):
|
|
|
|
"""
|
|
|
|
Adds (name, value) to an update query for an ancestor model.
|
|
|
|
|
|
|
|
Updates are coalesced so that we only run one update query per ancestor.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
self.related_updates[model].append((column, value, placeholder))
|
|
|
|
except KeyError:
|
|
|
|
self.related_updates[model] = [(column, value, placeholder)]
|
|
|
|
|
|
|
|
def get_related_updates(self):
|
|
|
|
"""
|
|
|
|
Returns a list of query objects: one for each update required to an
|
|
|
|
ancestor model. Each query will have the same filtering conditions as
|
|
|
|
the current query but will only update a single table.
|
|
|
|
"""
|
|
|
|
if not self.related_updates:
|
|
|
|
return []
|
|
|
|
result = []
|
|
|
|
for model, values in self.related_updates.iteritems():
|
|
|
|
query = UpdateQuery(model, self.connection)
|
|
|
|
query.values = values
|
|
|
|
if self.related_ids:
|
|
|
|
query.add_filter(('pk__in', self.related_ids))
|
|
|
|
result.append(query)
|
|
|
|
return result
|
|
|
|
|
|
|
|
class InsertQuery(Query):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super(InsertQuery, self).__init__(*args, **kwargs)
|
|
|
|
self.columns = []
|
|
|
|
self.values = []
|
|
|
|
self.params = ()
|
|
|
|
|
|
|
|
def clone(self, klass=None, **kwargs):
|
|
|
|
extras = {'columns': self.columns[:], 'values': self.values[:],
|
|
|
|
'params': self.params}
|
|
|
|
return super(InsertQuery, self).clone(klass, extras)
|
|
|
|
|
|
|
|
def as_sql(self):
|
|
|
|
# We don't need quote_name_unless_alias() here, since these are all
|
|
|
|
# going to be column names (so we can avoid the extra overhead).
|
|
|
|
qn = self.connection.ops.quote_name
|
|
|
|
result = ['INSERT INTO %s' % qn(self.model._meta.db_table)]
|
|
|
|
result.append('(%s)' % ', '.join([qn(c) for c in self.columns]))
|
|
|
|
result.append('VALUES (%s)' % ', '.join(self.values))
|
|
|
|
return ' '.join(result), self.params
|
|
|
|
|
|
|
|
def execute_sql(self, return_id=False):
|
|
|
|
cursor = super(InsertQuery, self).execute_sql(None)
|
|
|
|
if return_id:
|
|
|
|
return self.connection.ops.last_insert_id(cursor,
|
|
|
|
self.model._meta.db_table, self.model._meta.pk.column)
|
|
|
|
|
|
|
|
def insert_values(self, insert_values, raw_values=False):
|
|
|
|
"""
|
|
|
|
Set up the insert query from the 'insert_values' dictionary. The
|
|
|
|
dictionary gives the model field names and their target values.
|
|
|
|
|
|
|
|
If 'raw_values' is True, the values in the 'insert_values' dictionary
|
|
|
|
are inserted directly into the query, rather than passed as SQL
|
|
|
|
parameters. This provides a way to insert NULL and DEFAULT keywords
|
|
|
|
into the query, for example.
|
|
|
|
"""
|
|
|
|
placeholders, values = [], []
|
|
|
|
for field, val in insert_values:
|
|
|
|
if hasattr(field, 'get_placeholder'):
|
|
|
|
# Some fields (e.g. geo fields) need special munging before
|
|
|
|
# they can be inserted.
|
|
|
|
placeholders.append(field.get_placeholder(val))
|
|
|
|
else:
|
|
|
|
placeholders.append('%s')
|
|
|
|
|
|
|
|
self.columns.append(field.column)
|
|
|
|
values.append(val)
|
|
|
|
if raw_values:
|
|
|
|
self.values.extend(values)
|
|
|
|
else:
|
|
|
|
self.params += tuple(values)
|
|
|
|
self.values.extend(placeholders)
|
|
|
|
|
|
|
|
class DateQuery(Query):
|
|
|
|
"""
|
|
|
|
A DateQuery is a normal query, except that it specifically selects a single
|
|
|
|
date field. This requires some special handling when converting the results
|
|
|
|
back to Python objects, so we put it in a separate class.
|
|
|
|
"""
|
|
|
|
def results_iter(self):
|
|
|
|
"""
|
|
|
|
Returns an iterator over the results from executing this query.
|
|
|
|
"""
|
|
|
|
resolve_columns = hasattr(self, 'resolve_columns')
|
|
|
|
if resolve_columns:
|
|
|
|
from django.db.models.fields import DateTimeField
|
|
|
|
fields = [DateTimeField()]
|
|
|
|
else:
|
|
|
|
from django.db.backends.util import typecast_timestamp
|
|
|
|
needs_string_cast = self.connection.features.needs_datetime_string_cast
|
|
|
|
|
|
|
|
offset = len(self.extra_select)
|
|
|
|
for rows in self.execute_sql(MULTI):
|
|
|
|
for row in rows:
|
|
|
|
date = row[offset]
|
|
|
|
if resolve_columns:
|
|
|
|
date = self.resolve_columns([date], fields)[0]
|
|
|
|
elif needs_string_cast:
|
|
|
|
date = typecast_timestamp(str(date))
|
|
|
|
yield date
|
|
|
|
|
|
|
|
def add_date_select(self, column, lookup_type, order='ASC'):
|
|
|
|
"""
|
|
|
|
Converts the query into a date extraction query.
|
|
|
|
"""
|
|
|
|
alias = self.join((None, self.model._meta.db_table, None, None))
|
|
|
|
select = Date((alias, column), lookup_type,
|
|
|
|
self.connection.ops.date_trunc_sql)
|
|
|
|
self.select = [select]
|
|
|
|
self.select_fields = [None]
|
2008-04-28 08:20:40 +08:00
|
|
|
self.select_related = False # See #7097.
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
self.distinct = True
|
|
|
|
self.order_by = order == 'ASC' and [1] or [-1]
|
|
|
|
|
|
|
|
class CountQuery(Query):
|
|
|
|
"""
|
|
|
|
A CountQuery knows how to take a normal query which would select over
|
|
|
|
multiple distinct columns and turn it into SQL that can be used on a
|
|
|
|
variety of backends (it requires a select in the FROM clause).
|
|
|
|
"""
|
|
|
|
def get_from_clause(self):
|
|
|
|
result, params = self._query.as_sql()
|
|
|
|
return ['(%s) A1' % result], params
|
|
|
|
|
|
|
|
def get_ordering(self):
|
|
|
|
return ()
|