Did some more testing today...i left out the corrections for orderby. the attached patch includes that bit as well. sorry for the extra file.

i don't think there are any other clauses that you can put ID in, is that correct?

also, if anyone is using GAE development server with sqlite, key queries don't work properly there (spent 4 hours trying to figure out what i messed up to find out that others reported it as a bug to google).

christian

On 06/09/2010 08:54 PM, Christian Foster Howes wrote:
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 e21076a9427b gluon/contrib/gql.py
--- a/gluon/contrib/gql.py	Thu Jun 10 22:48:53 2010 -0500
+++ b/gluon/contrib/gql.py	Fri Jun 11 00:29:17 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
 
@@ -297,7 +298,7 @@
 
     def __or__(self, other):  # for use in sortby
         assert_filter_fields(self, other)
-        return Expression(self.name + '|' + other.name, None, None)
+        return Expression(self.name if self.type!='id' else '__key__' + '|' + other.name if other.type!='id' else '__key__', None, None)
 
     def __invert__(self):
         assert_filter_fields(self)
@@ -532,11 +533,24 @@
             assert_filter_fields(left)
             if left.type == '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):
+                    #get key (or keys) based on path.  Note if we later support
+                    # ancesters this will not be the proper key for items with
+                    # ancesters.
+                    #in GAE (with no ancesters) the key is base64 encoded
+                    # "table_name: id=<id>".  GAE decodes the string and compares
+                    # the id
+                    if op=='IN':
+                        right = [Key.from_path(left._tablename, r) for r in right]
+                    else:
+                        right = Key.from_path(left._tablename, right)
             elif op=='IN':
                 right = [dateobj_to_datetime(obj_represent(r, left.type, left._db)) \
                              for r in right]
@@ -634,20 +648,29 @@
             self.where = Query(fields[0].table.id,'>',0)
         for filter in self.where.filters:
             if filter.all():
+                #this is id > 0
                 continue
             elif filter.one() and filter.right<=0:
+                #this is id == 0
                 items = []
             elif filter.one():
+                #this is id == x
                 item = self._db[tablename]._tableobj.get_by_id(filter.right)
                 items = (item and [item]) or []
             elif isinstance(items,list):
-                (name, op, value) = (filter.left.name, filter.op, filter.right)
+                (name, op, value) = \
+                       (filter.left.name if filter.left.type!='id' else '__key__',
+                        filter.op, filter.right)
                 if op == '=': op = '=='
                 if op == 'IN': op = 'in'
                 items = [item for item in items \
                              if eval("getattr(item,'%s') %s %s" % (name, op, repr(value)))]
             else:
-                (name, op, value) = (filter.left.name, filter.op, filter.right)
+                (name, op, value) = \
+                       (filter.left.name if filter.left.type!='id' else '__key__',
+                        filter.op, filter.right)
+                if filter.left.type=='id':
+                    items.order("__key__")
                 items = items.filter('%s %s' % (name, op), value)
         if not isinstance(items,list):
             if attributes.get('left', None):
@@ -659,7 +682,11 @@
                 if isinstance(orderby, (list, tuple)):
                     orderby = gluon.sql.xorify(orderby)
                 assert_filter_fields(orderby)
-                orders = orderby.name.split('|')
+                if orderby.type == 'id':
+                    orders = ['__key__']
+                else:
+                    orders = orderby.name.split('|')
+                logging.info(orders)
                 for order in orders:
                     items = items.order(order)
             if attributes.get('limitby', None):
@@ -667,6 +694,7 @@
                 (limit, offset) = (lmax - lmin, lmin)
                 items = items.fetch(limit, offset=offset)
         fields = self._db[tablename].fields
+
         return (items, tablename, fields)
 
     def select(self, *fields, **attributes):

Reply via email to