Here is a new version of the patch to address the issues:
- no more __key__ in _extra (and in understand why it breaks backward compatibility now....just needed to think about it) - since it is illegal in web2py to have "_" start a field name, "__key__" can never be a field name and never queried on. so removed the support that i had tried to add in my previous version of the patch - since previously web2py required that id on GAE be a long, i continued that theme, which simplifies the code. basically the id>0 and id==x case were already special cases in the code, if you use any other operators on ID this version of the code detects field.type=='id' and will use "__key__" in the query (regardless of the name you have assigned your key, and with out my changing the name hack)
hopefully you agree that this is better, and thanks for your patience and code review! let me know if there are any other changes that i should make.
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 52be2b542a3c gluon/contrib/gql.py --- a/gluon/contrib/gql.py Wed Jun 09 10:52:12 2010 -0500 +++ b/gluon/contrib/gql.py Thu Jun 10 07:22:08 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 @@ -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,27 @@ 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) items = items.filter('%s %s' % (name, op), value) if not isinstance(items,list): if attributes.get('left', None): @@ -667,6 +688,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):