1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

[soc2010/query-refactor] Cleaned up implementation of negation in MongoDB, and no longer rely on a feature from MongoDB unstable version.

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/query-refactor@13368 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Alex Gaynor 2010-06-21 16:11:17 +00:00
parent dd5e718296
commit 706b18966f
5 changed files with 34 additions and 33 deletions

View File

@ -10,6 +10,7 @@ from django.utils.importlib import import_module
class DatabaseFeatures(object):
interprets_empty_strings_as_nulls = False
typed_columns = False
sql_nulls = False
class DatabaseOperations(object):

View File

@ -1,3 +1,6 @@
from django.db.models.sql.datastructures import FullResultSet
# TODO: ...
class SQLCompiler(object):
def __init__(self, query, connection, using):
@ -5,7 +8,7 @@ class SQLCompiler(object):
self.connection = connection
self.using = using
def get_filters(self, where, correct=False):
def get_filters(self, where):
assert where.connector == "AND"
filters = {}
for child in where.children:
@ -15,13 +18,15 @@ class SQLCompiler(object):
if k in filters:
v = {"$and": [filters[k], v]}
if where.negated:
v = {"$not": v}
filters[k] = v
filters.update(self.negate(k, v))
else:
filters[k] = v
else:
field, val = self.make_atom(*child, **{"negated": where.negated})
filters[field] = val
if correct:
self.correct_filters(filters)
try:
field, val = self.make_atom(*child, **{"negated": where.negated})
filters[field] = val
except FullResultSet:
pass
return filters
def make_atom(self, lhs, lookup_type, value_annotation, params_or_value, negated):
@ -48,17 +53,10 @@ class SQLCompiler(object):
val = {"$not": val}
return column, val
elif lookup_type == "lt":
if negated:
return {"$gte": params[0]}
return column, {"$lt": params[0]}
def correct_filters(self, filters):
for k, v in filters.items():
if isinstance(v, dict) and v.keys() == ["$not"]:
if isinstance(v["$not"], dict) and v["$not"].keys() == ["$and"]:
del filters[k]
or_vals = [self.negate(k, v) for v in v["$not"]["$and"]]
assert "$or" not in filters
filters["$or"] = or_vals
def negate(self, k, v):
if isinstance(v, dict):
if v.keys() == ["$not"]:
@ -76,7 +74,7 @@ class SQLCompiler(object):
assert self.query.high_mark is None
assert not self.query.order_by
filters = self.get_filters(self.query.where, correct=True)
filters = self.get_filters(self.query.where)
return self.connection.db[self.query.model._meta.db_table].find(filters)
def results_iter(self):

View File

@ -1077,7 +1077,11 @@ class Query(object):
# it's short-circuited in the Where class.
# We also need to handle the case where a subquery is provided
entry = self.where_class()
entry.add((Constraint(alias, col, None), 'isnull', True), AND)
entry.add((
Constraint(alias, col, None, eliminatable_if=lambda connection: not getattr(connection.features, "sql_nulls", True)),
'isnull',
True
), AND)
entry.negate()
self.where.add(entry, AND)

View File

@ -267,8 +267,9 @@ class Constraint(object):
An object that can be passed to WhereNode.add() and knows how to
pre-process itself prior to including in the WhereNode.
"""
def __init__(self, alias, col, field):
def __init__(self, alias, col, field, eliminatable_if=None):
self.alias, self.col, self.field = alias, col, field
self.elimintable_if = eliminatable_if
def __getstate__(self):
"""Save the state of the Constraint for pickling.
@ -321,6 +322,9 @@ class Constraint(object):
except ObjectDoesNotExist:
raise EmptyShortCircuit
if self.elimintable_if and self.elimintable_if(connection):
raise FullResultSet
return (self.alias, self.col, db_type), params
def relabel_aliases(self, change_map):

View File

@ -62,20 +62,8 @@ class MongoTestCase(TestCase):
q = Group.objects.create(name="Queen", year_formed=1971)
e = Group.objects.create(name="The E Street Band", year_formed=1972)
qs = Group.objects.exclude(year_formed=1972)
v = qs.query.get_compiler(qs.db).get_filters(qs.query.where, correct=True)
self.assertEqual(v, {
"$or": [
{"year_formed": {"$ne": 1972}},
{"year_formed": None},
]
})
# A bug in MongoDB prevents this query from actually working, but test
# that we're at least generating the right query.
return
self.assertQuerysetEqual(
qs, [
Group.objects.exclude(year_formed=1972), [
"Queen",
],
lambda g: g.name,
@ -105,4 +93,10 @@ class MongoTestCase(TestCase):
[],
lambda g: g.name
)
self.assertQuerysetEqual(
Group.objects.exclude(year_formed__lt=1972), [
"The E Street Band"
],
lambda g: g.name,
)