Hi all,

attached is a patch for gql.py. Please review and Massimo, if people like this, can we add it to trunk?

what does it do? it allows you to do key queries on Google App Engine. this means that you can now perform all ID queries on tables in google app engine. for example:

    belongs = db(db.test_tbl.id.belongs([69126,69127])).select()
    eq = db(db.test_tbl.id==69126).select()
    neq = db(db.test_tbl.id!=69126).select()
    lt = db(db.test_tbl.id<69126).select()
    gt = db(db.test_tbl.id>69126).select()
    lte = db(db.test_tbl.id<=69126).select()
    gte = db(db.test_tbl.id>=69126).select()
    all = db(db.test_tbl.id>0).select()

it also adds "__key__" to _extra on each row in the result...just in case you really wanted to see that google key.

if i missed some test cases please let me know and i'll verify them as well.

thanks!

Christian
diff -r 52be2b542a3c gluon/contrib/gql.py
--- a/gluon/contrib/gql.py	Wed Jun 09 10:52:12 2010 -0500
+++ b/gluon/contrib/gql.py	Wed Jun 09 20:48:24 2010 -0700
@@ -27,6 +27,7 @@
 import gluon.sql
 from new import classobj
 from google.appengine.ext import db as gae
+from google.appengine.api.datastore_types import Key
 
 MAX_ITEMS = 1000 # GAE main limitation
 
@@ -531,12 +532,33 @@
             # normal filter: field op value
             assert_filter_fields(left)
             if left.type == 'id':
+              #if the name is also id, treat it like an long
+              if left.name == 'id':
                 try:
-                    right = long(right or 0)
+                    if type(right) == list:
+                        #make this work for belongs
+                        right = [long(r) for r in right]
+                    else:
+                        right = long(right or 0)
                 except ValueError:
                     raise SyntaxError, 'id value must be integer: %s' % id
-                if not (op == '=' or (op == '>' and right == 0)):
-                    raise RuntimeError, '(field.id <op> value) is not supported on GAE'
+                if op != '=' and not (op == '>' and right == 0):
+                    #in this case we need to go a GAE __key__ query.  change the
+                    # name for query generation (we change the name back after the
+                    # query is generated)
+                    left.name="__key__"
+                    #get key (or keys) based on path.  Note if we later support
+                    # ancesters this will not be the proper key for items with
+                    # ancesters.
+                    if op=='IN':
+                        right = [Key.from_path(left._tablename, r) for r in right]
+                    else:
+                        right = Key.from_path(left._tablename, right)
+              else:
+                #this most likely means that the user created a field called
+                # __key__ that they want to use to do a query with a known
+                # key
+                right = Key(str(right or 0))
             elif op=='IN':
                 right = [dateobj_to_datetime(obj_represent(r, left.type, left._db)) \
                              for r in right]
@@ -667,6 +689,11 @@
                 (limit, offset) = (lmax - lmin, lmin)
                 items = items.fetch(limit, offset=offset)
         fields = self._db[tablename].fields
+
+        #change the id name back to id from __key__ used in the query
+        id_field = self._db[tablename]['id']
+        id_field.name='id'
+        
         return (items, tablename, fields)
 
     def select(self, *fields, **attributes):
@@ -683,10 +710,17 @@
             for t in fields:
                 if t == 'id':
                     new_item.append(int(item.key().id()))
+                elif t == '__key__':
+                    #actually want the key value from GAE
+                    new_item.append(item.key())
                 else:
                     new_item.append(getattr(item, t))
+            #add the key so it will be in _extra for the result
+            new_item.append(item.key())
             rows.append(new_item)
         colnames = ['%s.%s' % (tablename, t) for t in fields]
+        #add __key__ name to extra
+        colnames.append('__key__')
         return self.parse(self._db, rows, colnames, False, SetClass=Set)
 
     @staticmethod

Reply via email to