mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59:13 +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:
parent
dd5e718296
commit
706b18966f
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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,
|
||||
@ -106,3 +94,9 @@ class MongoTestCase(TestCase):
|
||||
lambda g: g.name
|
||||
)
|
||||
|
||||
self.assertQuerysetEqual(
|
||||
Group.objects.exclude(year_formed__lt=1972), [
|
||||
"The E Street Band"
|
||||
],
|
||||
lambda g: g.name,
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user