views:

809

answers:

6

or a simple way to iterate over columnName, value pairs?

My version of sqlalchemy is 0.5.6

Here is the sample code where I tried using dict(row), but it throws exception , TypeError: 'User' object is not iterable

import sqlalchemy
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

print "sqlalchemy version:",sqlalchemy.__version__ 

engine = create_engine('sqlite:///:memory:', echo=False)
metadata = MetaData()
users_table = Table('users', metadata,
     Column('id', Integer, primary_key=True),
     Column('name', String),
)
metadata.create_all(engine) 

class User(declarative_base()):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    name = Column(String)

    def __init__(self, name):
        self.name = name

Session = sessionmaker(bind=engine)
session = Session()

user1 = User("anurag")
session.add(user1)
session.commit()

# uncommenting next line throws exception 'TypeError: 'User' object is not iterable'
#print dict(user1)
# this one also throws 'TypeError: 'User' object is not iterable'
for u in session.query(User).all():
    print dict(u)

Running this code on my system outputs:

sqlalchemy version: 0.5.6
Traceback (most recent call last):
  File "untitled-1.py", line 37, in <module>
    print dict(u)
TypeError: 'User' object is not iterable
+1  A: 
for row in resultproxy:
    row_as_dict = dict(row)
Alex Brasetvik
It says 'XXX object is not iterable', I am using 0.5.6, i get by session.query(Klass).filter().all()
Anurag Uniyal
@Anurag Uniyal: Please update your question with (a) the smallest piece of code that shows the problem and (b) the real error traceback. It's very hard to do this through comments and summaries of the code.
S.Lott
Then you're using the ORM, which is entirely different from your actual question. This is a pretty basic thing which is thoroughly covered in the documentation: http://www.sqlalchemy.org/docs/05/ormtutorial.html
Alex Brasetvik
Please see the updated code, I am using same example given in the links you have mentioned, I also see no use of dict there?
Anurag Uniyal
+5  A: 

For now I am using

def row2dict(row):
    d = {}
    for columnName in row.__table__.columns.keys():
        d[columnName] = getattr(row, columnName)

    return d
Anurag Uniyal
In my own code, more or less the same: `User` has methods `def __iter__(self): self._i = iter(object_mapper(self).columns); return self` and `def next(self): n = self._i.next().name; return n, getattr(self, n)`. Then `dict(user1)` works.
stephan
A: 

The expression you are iterating through evaluates to list of model objects, not rows. So the following is correct usage of them:

for u in session.query(User).all():
    print u.id, u.name

Do you realy need to convert them to dicts? Sure, there is a lot of ways, but then you don't need ORM part of SQLAlchemy:

result = session.execute(User.__table__.select())
for row in result:
    print dict(row)

Update: Take a look at sqlalchemy.orm.attributes module. It has a set of functions to work with object state, that might be useful for you, especially instance_dict().

Denis Otkidach
I want to convert them to dict to, because some other code needs data as dict, and i want a generic way because I will not know what columns a model object have
Anurag Uniyal
and when I get handle to them I have access to model objects only so i can't use session.execute etc
Anurag Uniyal
A: 
class User(object):
    def to_dict(self):
        return dict([(k, getattr(self, k)) for k in self.__dict__.keys() if not k.startswith("_")])

That should work.

Singletoned
what happens if column name starts with "_" ?
Anurag Uniyal
I would imagine that you really shouldn't name your columns with a leading underscore. If you do, it won't work.If it's just the odd one, that you know about, you could modify it to add those columns.
Singletoned
+2  A: 
from sqlalchemy.orm import class_mapper

def asdict(obj):
    return dict((col.name, getattr(obj, col.name))
                for col in class_mapper(obj.__class__).mapped_table.c)
Marco Mariani
+1  A: 

I have a variation on Marco Mariani's answer, expressed as a decorator. The main difference is that it'll handle lists of entities, as well as safely ignoring some other types of return values (which is very useful when writing tests using mocks):

@decorator
def to_dict(f, *args, **kwargs):
  result = f(*args, **kwargs)
  if is_iterable(result) and not is_dict(result):
    return map(asdict, result)

  return asdict(result)

def asdict(obj):
  return dict((col.name, getattr(obj, col.name))
              for col in class_mapper(obj.__class__).mapped_table.c)

def is_dict(obj):
  return isinstance(obj, dict)

def is_iterable(obj):
  return True if getattr(obj, '__iter__', False) else False
Chris R