Ok. The basic idea is to allow you to define helpers methods on rows,
sort of like the Models of rails/django.
You use it like this... I put this in models/db_methods.py:
@extra_db_methods
class Users():
def name(self):
return '%s %s' % ((self.first_name or ''),
(self.last_name or ''))
def fb_name(self):
p = self.person()
return (p and
p.name) or 'Unknown dude'
def person(self):
return db.people(db.people.fb_id == self.fb_id)
def friends(self):
return [Storage(name=f[0], id=f[1])
for f in sj.loads(self.friends_cache)]
@extra_db_methods
class People():
... etc
These are for tables db.users and db.people. It looks up the table
name from the class name. For each table that you want to extend, you
make a class and put @extra_db_methods on top.
It's implemented with the following @extra_db_methods decorator and a
patch to dal.py. The decorator just traverses the class, pulls out all
methods, and throws them into a "methods" variable on the appropriate
table in dal. Then the dal's parse() routine adds these methods each
row object, using the python type.MethodType() routine for
retargetting a method from one class to another object.
The downside is extending dal with yet ANOTHER way of adding methods
to objects. That makes 3 apis to maintain for similar things
(virtualfields, computedfields, and this). And I'm not sure about the
names (like "extra_db_methods") for these things yet. Also I think we
might be able to get it even faster by being more clever with python
inheritance in the Row class. Right now it has roughly 10% overhead on
selects in my tests (uncompiled code).
At the bottom of this message is the decorator that implements the
same functionality using the existing virtualfields mechanism and your
"lazy" decorator. Its downside is a 2x to 3x overhead on selects and
instead of self.field you have to say self.<tablename>.field in the
method bodies.
def extra_db_methods(clss):
tablename = clss.__name__.lower()
if not tablename in db:
raise Error('There is no `%s\' table to put virtual methods in'
% tablename)
for k in clss.__dict__.keys():
method = clss.__dict__[k]
if type(method).__name__ == 'function' or type(method).__name__
== 'instancemethod':
db[tablename].methods.update({method.__name__ : method})
return clss
--- k/web2py/gluon/dal.py 2011-08-03 16:46:39.000000000 -0700
+++ web2py/gluon/dal.py 2011-08-10 17:04:48.344795251 -0700
@@ -1459,6 +1459,7 @@
new_rows.append(new_row)
rowsobj = Rows(db, new_rows, colnames, rawrows=rows)
for tablename in virtualtables:
+ rowsobj.setmethods(tablename, db[tablename].methods)
for item in db[tablename].virtualfields:
try:
rowsobj =
rowsobj.setvirtualfields(**{tablename:item})
@@ -4559,6 +4560,7 @@
tablename = tablename
self.fields = SQLCallableList()
self.virtualfields = []
+ self.methods = {}
fields = list(fields)
if db and self._db._adapter.uploads_in_blob==True:
@@ -5574,6 +5576,14 @@
self.compact = compact
self.response = rawrows
+ def setmethods(self, tablename, methods):
+ if len(methods) < 0: return
+ for row in self.records:
+ if tablename not in row: break # Abort on this and all
rows. For efficiency.
+ for (k,v) in methods.items():
+ r = row[tablename]
+ r.__dict__[k] = types.MethodType(v, r)
+ return self
def setvirtualfields(self,**keyed_virtualfields):
if not keyed_virtualfields:
return self
---
And Here's the implementation using virtualfields:
def lazy(f):
def g(self,f=f):
import copy
self=copy.copy(self)
return lambda *a,**b: f(self,*a,**b)
return g
def extra_db_methods_vf(clss):
''' This decorator clears virtualfields on the table and replaces
them with the methods on this class.
'''
# First let's make the methods lazy
for k in clss.__dict__.keys():
if type(getattr(clss, k)).__name__ == 'instancemethod':
setattr(clss, k, lazy(getattr(clss, k)))
tablename = clss.__name__.lower()
if not tablename in db:
raise Error('There is no `%s\' table to put virtual methods in'
% tablename)
del db[tablename].virtualfields[:] # We clear virtualfields each
time
db[tablename].virtualfields.append(clss())
return clss
You use this just like before but with @extra_db_methods_vf instead of
@extra_db_methods, and append <tablename> to each use of "self".
On Aug 9, 11:16 pm, Massimo Di Pierro <
massimo.dipie...@gmail.com>
wrote:
> let us see it!
>
> On Aug 9, 9:36 pm, MichaelToomim<
too...@gmail.com> wrote:
>
>
>
>
>
>
>
> > Result: Fixed by upgrading. I was seeing this bug:
http://code.google.com/p/web2py/issues/detail?id=345
>
> > However, virtualfields still take more time than they should. My
> > selects take 2-3x longer with virtualfields enabled than without. I
> > implemented a little hack in the dal that adds methods to rows with
> > only a 10% overhead (instead of 200-300%) and can share that if
> > anyone's interested.
>