Author: Alex
Date: 2010-06-18 20:26:36 -0500 (Fri, 18 Jun 2010)
New Revision: 13358

Modified:
   django/branches/soc2010/query-refactor/django/contrib/mongodb/compiler.py
   
django/branches/soc2010/query-refactor/tests/regressiontests/mongodb/models.py
   django/branches/soc2010/query-refactor/tests/regressiontests/mongodb/tests.py
Log:
[soc2010/query-refactor] Implemented not equal (exclude(foo=bar)) in the ORM 
for MongoDB, note that this doesn't actually work at the moment due to a bug in 
MongoDB.

Modified: 
django/branches/soc2010/query-refactor/django/contrib/mongodb/compiler.py
===================================================================
--- django/branches/soc2010/query-refactor/django/contrib/mongodb/compiler.py   
2010-06-18 02:04:10 UTC (rev 13357)
+++ django/branches/soc2010/query-refactor/django/contrib/mongodb/compiler.py   
2010-06-19 01:26:36 UTC (rev 13358)
@@ -5,21 +5,27 @@
         self.connection = connection
         self.using = using
     
-    def get_filters(self, where):
+    def get_filters(self, where, correct=False):
         assert where.connector == "AND"
-        assert not where.negated
         filters = {}
         for child in where.children:
             if isinstance(child, self.query.where_class):
-                # TODO: probably needs to check for dupe keys
-                filters.update(self.get_filters(child))
+                child_filters = self.get_filters(child)
+                for k, v in child_filters.iteritems():
+                    if k in filters:
+                        v = {"$and": [filters[k], v]}
+                    if where.negated:
+                        v = {"$not": v}
+                    filters[k] = v
             else:
-                field, val = self.make_atom(*child)
+                field, val = self.make_atom(*child, **{"negated": 
where.negated})
                 filters[field] = val
+        if correct:
+            self.correct_filters(filters)
         return filters
     
-    def make_atom(self, lhs, lookup_type, value_annotation, params_or_value):
-        assert lookup_type == "exact"
+    def make_atom(self, lhs, lookup_type, value_annotation, params_or_value, 
negated):
+        assert lookup_type in ["exact", "isnull"], lookup_type
         if hasattr(lhs, "process"):
             lhs, params = lhs.process(lookup_type, params_or_value, 
self.connection)
         else:
@@ -30,8 +36,34 @@
         assert table == self.query.model._meta.db_table
         if column == self.query.model._meta.pk.column:
             column = "_id"
-        return column, params[0]
+        
+        if lookup_type == "exact":
+            val = params[0]
+            if negated:
+                val = {"$ne": val}
+            return column, val
+        elif lookup_type == "isnull":
+            val = None
+            if value_annotation == negated:
+                val = {"$not": val}
+            return column, val
     
+    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"]:
+                return {k: v["$not"]}
+            return {k: {"$not": v}}
+        return {k: {"$ne": v}}
+    
     def build_query(self, aggregates=False):
         assert len([a for a in self.query.alias_map if 
self.query.alias_refcount[a]]) <= 1
         if not aggregates:
@@ -42,7 +74,7 @@
         assert self.query.high_mark is None
         assert not self.query.order_by
         
-        filters = self.get_filters(self.query.where)
+        filters = self.get_filters(self.query.where, correct=True)
         return 
self.connection.db[self.query.model._meta.db_table].find(filters)
     
     def results_iter(self):

Modified: 
django/branches/soc2010/query-refactor/tests/regressiontests/mongodb/models.py
===================================================================
--- 
django/branches/soc2010/query-refactor/tests/regressiontests/mongodb/models.py  
    2010-06-18 02:04:10 UTC (rev 13357)
+++ 
django/branches/soc2010/query-refactor/tests/regressiontests/mongodb/models.py  
    2010-06-19 01:26:36 UTC (rev 13358)
@@ -15,3 +15,5 @@
 class Group(models.Model):
     id = models.NativeAutoField(primary_key=True)
     name = models.CharField(max_length=255)
+    year_formed = models.IntegerField(null=True)
+

Modified: 
django/branches/soc2010/query-refactor/tests/regressiontests/mongodb/tests.py
===================================================================
--- 
django/branches/soc2010/query-refactor/tests/regressiontests/mongodb/tests.py   
    2010-06-18 02:04:10 UTC (rev 13357)
+++ 
django/branches/soc2010/query-refactor/tests/regressiontests/mongodb/tests.py   
    2010-06-19 01:26:36 UTC (rev 13358)
@@ -57,3 +57,26 @@
         self.assertEqual(b.current_group_id, e.pk)
         self.assertFalse(hasattr(b, "_current_group_cache"))
         self.assertEqual(b.current_group, e)
+    
+    def test_lookup(self):
+        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, [
+                "Queen",
+            ],
+            lambda g: g.name,
+        )

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to django-upda...@googlegroups.com.
To unsubscribe from this group, send email to 
django-updates+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to