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):