1
0
mirror of https://github.com/django/django.git synced 2025-07-07 03:09:22 +00:00

querset-refactor: Fixed a bunch of little things as a result of working on the

tests.

All of modeltests/ now pass. This includes a fix to modeltests/signals/ (in the
post_delete signal testing), which I'm sure was incorrect previously.


git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6489 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2007-10-14 02:13:51 +00:00
parent e9f1f50461
commit b47231ee3e
6 changed files with 33 additions and 35 deletions

View File

@ -300,7 +300,7 @@ class _QuerySet(object):
if kwargs: if kwargs:
clone.query.add_q(mapper(Q(**kwargs))) clone.query.add_q(mapper(Q(**kwargs)))
for arg in args: for arg in args:
clone.query.add_q(arg) clone.query.add_q(mapper(arg))
return clone return clone
def complex_filter(self, filter_obj): def complex_filter(self, filter_obj):
@ -515,7 +515,7 @@ def delete_objects(seen_objs):
update_query = sql.UpdateQuery(cls, connection) update_query = sql.UpdateQuery(cls, connection)
for field in cls._meta.fields: for field in cls._meta.fields:
if field.rel and field.null and field.rel.to in seen_objs: if field.rel and field.null and field.rel.to in seen_objs:
update_query.clear_related(field.column, pk_list) update_query.clear_related(field, pk_list)
# Now delete the actual data # Now delete the actual data
for cls in ordered_classes: for cls in ordered_classes:

View File

@ -54,7 +54,6 @@ MULTI = 'multi'
SINGLE = 'single' SINGLE = 'single'
NONE = None NONE = None
# FIXME: Add quote_name() calls around all the tables.
class Query(object): class Query(object):
""" """
A single SQL query. A single SQL query.
@ -176,6 +175,7 @@ class Query(object):
If 'with_limits' is False, any limit/offset information is not included If 'with_limits' is False, any limit/offset information is not included
in the query. in the query.
""" """
qn = self.connection.ops.quote_name
self.pre_sql_setup() self.pre_sql_setup()
result = ['SELECT'] result = ['SELECT']
if self.distinct: if self.distinct:
@ -192,10 +192,10 @@ class Query(object):
alias_str = (alias != name and ' AS %s' % alias or '') alias_str = (alias != name and ' AS %s' % alias or '')
if join_type: if join_type:
result.append('%s %s%s ON (%s.%s = %s.%s)' result.append('%s %s%s ON (%s.%s = %s.%s)'
% (join_type, name, alias_str, lhs, lhs_col, alias, % (join_type, qn(name), alias_str, qn(lhs),
col)) qn(lhs_col), qn(alias), qn(col)))
else: else:
result.append('%s%s' % (name, alias_str)) result.append('%s%s' % (qn(name), alias_str))
result.extend(self.extra_tables) result.extend(self.extra_tables)
where, params = self.where.as_sql() where, params = self.where.as_sql()
@ -282,12 +282,12 @@ class Query(object):
elif self.where: elif self.where:
# rhs has an empty where clause. Make it match everything (see # rhs has an empty where clause. Make it match everything (see
# above for reasoning). # above for reasoning).
w = WhereNode() w = WhereNode(self)
alias = self.join((None, self.model._meta.db_table, None, None)) alias = self.join((None, self.model._meta.db_table, None, None))
pk = self.model._meta.pk pk = self.model._meta.pk
w.add((alias, pk.column, pk, 'isnull', False), AND) w.add((alias, pk.column, pk, 'isnull', False), AND)
else: else:
w = WhereNode() w = WhereNode(self)
self.where.add(w, connection) self.where.add(w, connection)
# Selection columns and extra extensions are those provided by 'rhs'. # Selection columns and extra extensions are those provided by 'rhs'.
@ -334,7 +334,7 @@ class Query(object):
result.append(col.as_sql(quote_func=qn)) result.append(col.as_sql(quote_func=qn))
else: else:
table_alias = self.tables[0] table_alias = self.tables[0]
result = ['%s.%s' % (table_alias, qn(f.column)) result = ['%s.%s' % (qn(table_alias), qn(f.column))
for f in self.model._meta.fields] for f in self.model._meta.fields]
# We sort extra_select so that the result columns are in a well-defined # We sort extra_select so that the result columns are in a well-defined
@ -388,10 +388,10 @@ class Query(object):
order = 'ASC' order = 'ASC'
if '.' in col: if '.' in col:
table, col = col.split('.', 1) table, col = col.split('.', 1)
table = '%s.' % self.table_alias[table] table = '%s.' % qn(self.table_alias(table)[0])
elif col not in self.extra_select: elif col not in self.extra_select:
# Use the root model's database table as the referenced table. # Use the root model's database table as the referenced table.
table = '%s.' % self.tables[0] table = '%s.' % qn(self.tables[0])
else: else:
table = '' table = ''
result.append('%s%s %s' % (table, result.append('%s%s %s' % (table,
@ -685,9 +685,9 @@ class Query(object):
if field.rel: if field.rel:
# One-to-one or many-to-one field # One-to-one or many-to-one field
remote_opts = field.rel.to._meta remote_opts = field.rel.to._meta
target = field.rel.get_related_field()
alias = self.join((root_alias, remote_opts.db_table, field.column, alias = self.join((root_alias, remote_opts.db_table, field.column,
field.rel.field_name)) target.column))
target = remote_opts.get_field(field.rel.field_name)
return [alias], remote_opts, field, target, target.column return [alias], remote_opts, field, target, target.column
# Only remaining possibility is a normal (direct lookup) field. No # Only remaining possibility is a normal (direct lookup) field. No
@ -838,8 +838,8 @@ class DeleteQuery(Query):
if not isinstance(related.field, generic.GenericRelation): if not isinstance(related.field, generic.GenericRelation):
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
where = WhereNode(self) where = WhereNode(self)
where.add((None, related.field.m2m_reverse_name(), None, where.add((None, related.field.m2m_reverse_name(),
'in', related.field, 'in',
pk_list[offset : offset+GET_ITERATOR_CHUNK_SIZE]), pk_list[offset : offset+GET_ITERATOR_CHUNK_SIZE]),
AND) AND)
self.do_query(related.field.m2m_db_table(), where) self.do_query(related.field.m2m_db_table(), where)
@ -913,9 +913,9 @@ class UpdateQuery(Query):
This is used by the QuerySet.delete_objects() method. This is used by the QuerySet.delete_objects() method.
""" """
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
where = WhereNode() where = WhereNode(self)
f = self.model._meta.pk f = self.model._meta.pk
where.add((None, f, f.db_type(), 'in', where.add((None, f.column, f, 'in',
pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]),
AND) AND)
values = [(related_field.column, 'NULL')] values = [(related_field.column, 'NULL')]

View File

@ -70,11 +70,10 @@ class WhereNode(tree.Node):
params = child[2].get_db_prep_lookup(child[3], child[4]) params = child[2].get_db_prep_lookup(child[3], child[4])
format = '%s' format = '%s'
except EmptyResultSet: except EmptyResultSet:
if node.negated: if self.connection == AND and not node.negated:
# If this is a "not" atom, being empty means it has no # We can bail out early in this particular case (only).
# effect on the result, so we can ignore it.
continue
raise raise
sql = None
if sql: if sql:
result.append(format % sql) result.append(format % sql)
result_params.extend(params) result_params.extend(params)
@ -92,10 +91,11 @@ class WhereNode(tree.Node):
""" """
table_alias, name, field, lookup_type, value = child table_alias, name, field, lookup_type, value = child
conn = self.query.connection conn = self.query.connection
qn = conn.ops.quote_name
if table_alias: if table_alias:
lhs = '%s.%s' % (table_alias, conn.ops.quote_name(name)) lhs = '%s.%s' % (qn(table_alias), qn(name))
else: else:
lhs = conn.ops.quote_name(name) lhs = qn(name)
db_type = field and field.db_type() or None db_type = field and field.db_type() or None
field_sql = conn.ops.field_cast_sql(db_type) % lhs field_sql = conn.ops.field_cast_sql(db_type) % lhs

View File

@ -145,18 +145,18 @@ False
[<Article: John's second story>, <Article: This is a test>] [<Article: John's second story>, <Article: This is a test>]
# The underlying query only makes one join when a related table is referenced twice. # The underlying query only makes one join when a related table is referenced twice.
>>> query = Article.objects.filter(reporter__first_name__exact='John', reporter__last_name__exact='Smith') >>> queryset = Article.objects.filter(reporter__first_name__exact='John', reporter__last_name__exact='Smith')
>>> null, sql, null = query._get_sql_clause() >>> sql = queryset.query.as_sql()[0]
>>> sql.count('INNER JOIN') >>> sql.count('INNER JOIN')
1 1
# The automatically joined table has a predictable name. # The automatically joined table has a predictable name.
>>> Article.objects.filter(reporter__first_name__exact='John').extra(where=["many_to_one_article__reporter.last_name='Smith'"]) >>> Article.objects.filter(reporter__first_name__exact='John').extra(where=["many_to_one_reporter.last_name='Smith'"])
[<Article: John's second story>, <Article: This is a test>] [<Article: John's second story>, <Article: This is a test>]
# And should work fine with the unicode that comes out of # And should work fine with the unicode that comes out of
# newforms.Form.cleaned_data # newforms.Form.cleaned_data
>>> Article.objects.filter(reporter__first_name__exact='John').extra(where=["many_to_one_article__reporter.last_name='%s'" % u'Smith']) >>> Article.objects.filter(reporter__first_name__exact='John').extra(where=["many_to_one_reporter.last_name='%s'" % u'Smith'])
[<Article: John's second story>, <Article: This is a test>] [<Article: John's second story>, <Article: This is a test>]
# Find all Articles for the Reporter whose ID is 1. # Find all Articles for the Reporter whose ID is 1.

View File

@ -4,11 +4,9 @@
To perform an OR lookup, or a lookup that combines ANDs and ORs, To perform an OR lookup, or a lookup that combines ANDs and ORs,
combine QuerySet objects using & and | operators. combine QuerySet objects using & and | operators.
Alternatively, use positional arguments, and pass one or more expressions Alternatively, use positional arguments, and pass one or more expressions of
of clauses using the variable ``django.db.models.Q`` (or any object with clauses using the variable ``django.db.models.Q`` (or any object with an
a get_sql method). add_to_query method).
""" """
from django.db import models from django.db import models

View File

@ -54,7 +54,7 @@ Is updated
pre_delete signal, Tom Smith pre_delete signal, Tom Smith
instance.id is not None: True instance.id is not None: True
post_delete signal, Tom Smith post_delete signal, Tom Smith
instance.id is None: False instance.id is None: True
>>> p2 = Person(first_name='James', last_name='Jones') >>> p2 = Person(first_name='James', last_name='Jones')
>>> p2.id = 99999 >>> p2.id = 99999
@ -73,7 +73,7 @@ Is created
pre_delete signal, James Jones pre_delete signal, James Jones
instance.id is not None: True instance.id is not None: True
post_delete signal, James Jones post_delete signal, James Jones
instance.id is None: False instance.id is None: True
>>> Person.objects.all() >>> Person.objects.all()
[<Person: James Jones>] [<Person: James Jones>]