Repository: web2py/web2py

Issue:
'Expression' object has no attribute 'tablename'
Has this problem been resolved:

https://groups.google.com/forum/#!topic/web2py/-6Xz48ECcEM


Still see this on
web2py™ | Version 2.15.3
-- | --
 rows = db(q).select(t.ALL, max, groupby=t.ALL, join=join) # this produces the error above :(
 rows = db(q).select(t.ALL, max, groupby=t.ALL, join=join).as_list() #this doesn't throw the error :)





'Expression' object has no attribute 'tablename'
Has this problem been resolved:

https://groups.google.com/forum/#!topic/web2py/-6Xz48ECcEM


Still see this on
web2py™ | Version 2.15.3
-- | --
 rows = db(q).select(t.ALL, max, groupby=t.ALL, join=join) # this produces the error above :(
 rows = db(q).select(t.ALL, max, groupby=t.ALL, join=join).as_list() #this doesn't throw the error :)







Discussion:
This error affects me as well (with PostgreSQL)

Version 2.15.4-stable+timestamp.2017.09.02.04.02.22

```
db.define_table('measurement',
    Field('title', label=T('measurement__title'), requires=IS_NOT_EMPTY()),
    Field('systemic', 'boolean', writable=False, readable=False, default=False),
    Field('sys_name', writable=False, readable=False),
    format='%(title)s'
)

title_field = db.translation_field.value.coalesce(db.measurement.title)
TOTAL_ROWS = 'COUNT(*) OVER()'

records = db(db.measurement).select(
        db.measurement.ALL, title_field, TOTAL_ROWS,
        left=[
            db.translation_field.on(
                (db.translation_field.tablename == 'measurement') &
                (db.translation_field.fieldname == 'title') &
                (db.translation_field.rid == db.measurement.id) &
                (db.translation_field.language_id == auth.user.language_id)
            )
        ],
        orderby=title_field
    )

# translate
for i, r in enumerate(records):
    records[i].measurement.title = r[title_field]

result = SQLTABLE(records, headers='labels', truncate=None, columns=['measurement.title')
```
Error:

```
Traceback (most recent call last):
  File "/vagrant/grand3/web2py/gluon/restricted.py", line 219, in restricted
    exec(ccode, environment)
  File "/vagrant/grand3/web2py/applications/app/controllers/admin.py", line 695, in <module>
  File "/vagrant/grand3/web2py/gluon/globals.py", line 414, in <lambda>
    self._caller = lambda f: f()
  File "/vagrant/grand3/web2py/gluon/tools.py", line 3981, in f
    return action(*a, **b)
  File "/vagrant/grand3/web2py/applications/app/controllers/admin.py", line 447, in measurements_table
    'content': _measurement_actions, 'selected': False}])
  File "/vagrant/grand3/web2py/gluon/sqlhtml.py", line 3330, in __init__
    tablemap = dict(((f.tablename, f.table) for f in fieldmap.values()))
  File "/vagrant/grand3/web2py/gluon/sqlhtml.py", line 3330, in <genexpr>
    tablemap = dict(((f.tablename, f.table) for f in fieldmap.values()))
AttributeError: 'Expression' object has no attribute 'tablename'
```
This error affects me as well (with PostgreSQL)

Version 2.15.4-stable+timestamp.2017.09.02.04.02.22

```
db.define_table('measurement',
    Field('title', label=T('measurement__title'), requires=IS_NOT_EMPTY()),
    Field('systemic', 'boolean', writable=False, readable=False, default=False),
    Field('sys_name', writable=False, readable=False),
    format='%(title)s'
)

title_field = db.translation_field.value.coalesce(db.measurement.title)
TOTAL_ROWS = 'COUNT(*) OVER()'

records = db(db.measurement).select(
        db.measurement.ALL, title_field, TOTAL_ROWS,
        left=[
            db.translation_field.on(
                (db.translation_field.tablename == 'measurement') &
                (db.translation_field.fieldname == 'title') &
                (db.translation_field.rid == db.measurement.id) &
                (db.translation_field.language_id == auth.user.language_id)
            )
        ],
        orderby=title_field
    )

# translate
for i, r in enumerate(records):
    records[i].measurement.title = r[title_field]

result = SQLTABLE(records, headers='labels', truncate=None, columns=['measurement.title')
```
Error:

```
Traceback (most recent call last):
  File "/vagrant/grand3/web2py/gluon/restricted.py", line 219, in restricted
    exec(ccode, environment)
  File "/vagrant/grand3/web2py/applications/app/controllers/admin.py", line 695, in <module>
  File "/vagrant/grand3/web2py/gluon/globals.py", line 414, in <lambda>
    self._caller = lambda f: f()
  File "/vagrant/grand3/web2py/gluon/tools.py", line 3981, in f
    return action(*a, **b)
  File "/vagrant/grand3/web2py/applications/app/controllers/admin.py", line 447, in measurements_table
    'content': _measurement_actions, 'selected': False}])
  File "/vagrant/grand3/web2py/gluon/sqlhtml.py", line 3330, in __init__
    tablemap = dict(((f.tablename, f.table) for f in fieldmap.values()))
  File "/vagrant/grand3/web2py/gluon/sqlhtml.py", line 3330, in <genexpr>
    tablemap = dict(((f.tablename, f.table) for f in fieldmap.values()))
AttributeError: 'Expression' object has no attribute 'tablename'
```

Resolving patch:
diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py
--- a/gluon/sqlhtml.py
+++ b/gluon/sqlhtml.py
@@ -3328,7 +3328,7 @@ def __init__(self,
             return
         REGEX_TABLE_DOT_FIELD = sqlrows.db._adapter.REGEX_TABLE_DOT_FIELD
         fieldmap = dict(zip(sqlrows.colnames, sqlrows.fields))
-        tablemap = dict(((f.tablename, f.table) for f in fieldmap.values()))
+        tablemap = dict(((f.tablename, f.table) if isinstance(f, Field) else (f._table._tablename, f._table) for f in fieldmap.values()))
         for table in tablemap.values():
             pref = table._tablename + '.'
             fieldmap.update(((pref+f.name, f) for f in table._virtual_fields))


Test patch:
diff --git a/gluon/tests/test_sqlhtml.py b/gluon/tests/test_sqlhtml.py
--- a/gluon/tests/test_sqlhtml.py
+++ b/gluon/tests/test_sqlhtml.py
@@ -355,10 +355,10 @@ def setUp(self):
 
         self.db.commit()
 
-#    def test_SQLTABLE(self):
-#        rows = self.db(self.db.auth_user.id > 0).select(self.db.auth_user.ALL)
-#        sqltable = SQLTABLE(rows)
-#        self.assertEqual(sqltable.xml(), '<table><thead><tr><th>auth_user.id</th><th>auth_user.first_name</th><th>auth_user.last_name</th><th>auth_user.email</th><th>auth_user.username</th><th>auth_user.password</th><th>auth_user.registration_key</th><th>auth_user.reset_password_key</th><th>auth_user.registration_id</th></tr></thead><tbody><tr class="w2p_odd odd"><td>1</td><td>Bart</td><td>Simpson</td><td>user1@test.com</td><td>user1</td><td>password_123</td><td>None</td><td></td><td>None</td></tr></tbody></table>')
+    def test_SQLTABLE(self):
+        rows = self.db(self.db.auth_user.id > 0).select(self.db.auth_user.ALL)
+        sqltable = SQLTABLE(rows)
+        self.assertEqual(sqltable.xml(), b'<table><thead><tr><th>auth_user.id</th><th>auth_user.first_name</th><th>auth_user.last_name</th><th>auth_user.email</th><th>auth_user.username</th><th>auth_user.password</th><th>auth_user.registration_key</th><th>auth_user.reset_password_key</th><th>auth_user.registration_id</th></tr></thead><tbody><tr class="w2p_odd odd"><td>1</td><td>Bart</td><td>Simpson</td><td>user1@test.com</td><td>user1</td><td>password_123</td><td>None</td><td></td><td>None</td></tr></tbody></table>')
 
 
 # class TestExportClass(unittest.TestCase):