Hi SQLAlchemy,
Our data team wants us to use a (SQL Server 2008 R2) stored procedure to
perform our major query, which is all well and good, except it's preventing
SQLAlchemy's type processing from being applied. This is on SQLAlchemy
0.9.1, using pyodbc and FreeTDS.
For example, say we are trying to map this class (using Flask-SQLAlchemy):
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
random_time = db.Column(UTCDateTime)
def __init__(self, username, email):
self.username = username
self.email = email
self.random_time = datetime.now()
Using this (trivial) user-defined type:
class UTCDateTime(db.TypeDecorator):
impl = db.DateTime
def process_result_value(self, value, dialect):
print "AWOOOOOGA"
return value
Create the table and populate it with some values:
db.create_all()
db.session.add(User('alice', '[email protected]'))
db.session.add(User('bob', '[email protected]'))
db.session.commit()
users = db.session.query(User).all()
Two AWOOGAs are output, as expected.
Then, create a stored procedure like this:
CREATE PROCEDURE "GetUser" AS
SELECT
*
FROM "user"
GO
And query into User objects using the procedure:
db.session.add(User('charlie', '[email protected]'))
db.session.commit()
text = db.text('exec getuser')
users = db.session.query(User).from_statement(text).all()
The resulting User objects look reasonable, *but no AWOOOOGAs*, and the
strings are all bytestrings.
After looking at the docs more closely, this isn't very surprising: text()
does warn about a lack of type processing, and suggests using
text().columns() to provide a mapping (in lieu of the now-deprecated
typemap kwarg to text()). This creates a TextAsFrom object, which adds some
extra superpowers to text() including a .c attribute. Problem is,
from_statement() doesn't like it:
db.session.commit()
typemap = {'id': db.Integer, 'username': db.String, 'email': db.String,
'random_time': UTCDateTime}
taf = text.columns(**typemap)
users = db.session.query(User).from_statement(taf).all()
This results in a stack trace:
AttributeError Traceback (most recent call last)
<ipython-input-20-c694595d6ec1> in <module>()
----> 1 users = db.session.query(User).from_statement(taf).all()
/home/badmin/.virtualenvs/bridge-it/local/lib/python2.7/site-packages/sqlalchemy/orm/query.pyc
in all(self)
2278
2279 """
-> 2280 return list(self)
2281
2282 @_generative(_no_clauseelement_condition)
/home/badmin/.virtualenvs/bridge-it/local/lib/python2.7/site-packages/sqlalchemy/orm/query.pyc
in __iter__(self)
2386
2387 def __iter__(self):
-> 2388 context = self._compile_context()
2389 context.statement.use_labels = True
2390 if self._autoflush and not self._populate_existing:
/home/badmin/.virtualenvs/bridge-it/local/lib/python2.7/site-packages/sqlalchemy/orm/query.pyc
in _compile_context(self, labels)
2732
2733 def _compile_context(self, labels=True):
-> 2734 context = QueryContext(self)
2735
2736 if context.statement is not None:
/home/badmin/.virtualenvs/bridge-it/local/lib/python2.7/site-packages/sqlalchemy/orm/query.pyc
in __init__(self, query)
3478 if query._statement is not None:
3479 if isinstance(query._statement, expression.SelectBase)
and \
-> 3480 not query._statement.use_labels:
3481 self.statement = query._statement.apply_labels()
3482 else:
AttributeError: 'TextAsFrom' object has no attribute 'use_labels'
Looks like TextAsFrom isn't quite select-like enough for from_statement().
I tried tacking on a taf.use_labels = True before running the query, but
just got another error:
NoSuchColumnError Traceback (most recent call last)
<ipython-input-23-c694595d6ec1> in <module>()
----> 1 users = db.session.query(User).from_statement(taf).all()
/home/badmin/.virtualenvs/bridge-it/local/lib/python2.7/site-packages/sqlalchemy/orm/query.pyc
in all(self)
2278
2279 """
-> 2280 return list(self)
2281
2282 @_generative(_no_clauseelement_condition)
/home/badmin/.virtualenvs/bridge-it/local/lib/python2.7/site-packages/sqlalchemy/orm/loading.pyc
in instances(query, cursor, context)
70 process[0](row, rows)
71 elif single_entity:
---> 72 rows = [process[0](row, None) for row in fetch]
73 else:
74 rows = [util.KeyedTuple([proc(row, None) for proc in
process],
/home/badmin/.virtualenvs/bridge-it/local/lib/python2.7/site-packages/sqlalchemy/orm/loading.pyc
in _instance(row, result)
358 identitykey = (
359 identity_class,
--> 360 tuple([row[column] for column in
pk_cols])
361 )
362
/home/badmin/.virtualenvs/bridge-it/local/lib/python2.7/site-packages/sqlalchemy/engine/result.pyc
in _key_fallback(self, key, raiseerr)
315 raise exc.NoSuchColumnError(
316 "Could not locate column in row for column
'%s'" %
--> 317 expression._string_or_unprintable(key))
318 else:
319 return None
NoSuchColumnError: "Could not locate column in row for column 'user.id'"
Any ideas? Incidentally, we can use the taf object in a session.execute()
and get great results back, type processing and all. Problem is, they're
just tuples (or a ResultProxy before you fetchall or iterate over it). Any
way to convince SQLAlchemy to turn that result set into User objects, or at
that point should we just send those to User() ourselves?
Thanks,
Matt
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/groups/opt_out.