Roland Pish
Roland Pish

Reputation: 815

Create dictionary of a sqlalchemy query object in Pyramid

I am new to Python and Pyramid. In a test application I am using to learn more about Pyramid, I want to query a database and create a dictionary based on the results of a sqlalchemy query object and finally send the dictionary to the chameleon template.

So far I have the following code (which works fine), but I wanted to know if there is a better way to create my dictionary.

...
index = 0
clients = {}
q = self.request.params['q']
for client in DBSession.query(Client).filter(Client.name.like('%%%s%%' % q)).all():
    clients[index] = { "id": client.id, "name": client.name }
    index += 1
output = { "clients": clients }

return output

While learning Python, I found a nice way to create a list in a for loop statement like the following:

myvar = [user.name for user in users]

So, the other question I had: is there a similar 'one line' way like the above to create a dictionary of a sqlalchemy query object?

Thanks in advance.

Upvotes: 1

Views: 1798

Answers (3)

Jonathan Vanasco
Jonathan Vanasco

Reputation: 15690

If you want to turn a SqlAlchemy object into a dict, you can use this code:

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

there is another attribute of the mapped table that has the relationships in it , but the code gets dicey.

you don't need to cast an object into a dict for any of the template libraries, but if you decide to persist the data ( memcached, session, pickle, etc ) you'll either need to use dicts or write some code to 'merge' the persisted data back into the session.

a quick side note- if you render any of this data through json , you'll either need to have a custom json renderer that can handle datetime objects , or change the values in a function.

Upvotes: 0

Sergey
Sergey

Reputation: 12437

@TokenMacGuy gave a nice and detailed answer to your question. However, I have a feeling you've asked a wrong question :)

You don't need to convert SQLALchemy objects to dictionaries before passing them to the template - that would be quite inconvenient. You can pass the result of a query as is and directly use SQLALchemy mapped objects in your template

q = self.request.params['q']
clients = DBSession.query(Client).filter(q in Client.name).all()
return {'clients': clients}

Upvotes: 3

SingleNegationElimination
SingleNegationElimination

Reputation: 156278

well, yes, we can tighten this up a bit.

First, this pattern:

 index = 0
 for item in seq:
     frobnicate(index, item)
     item += 1

is common enough that there's a builtin function that does it automatically, enumerate(), used like this:

 for index, item in enumerate(seq):
     frobnicate(index, item)

but, I'm not sure you need it, Associating things with an integer index starting from zero is the functionality of a list, you don't really need a dict for that; unless you want to have holes, or need some of the other special features of dicts, just do:

 stuff = []
 stuff.extend(seq)

when you're only interested in a small subset of the attributes of a database entity, it's a good idea to tell sqlalchemy to emit a query that returns only that:

 query = DBSession.query(Client.id, Client.name) \
                  .filter(q in Client.name)

In the above i've also shortened the .name.like('%%%s%%' % q) into just q in name since they mean the same thing (sqlalchemy expands it into the correct LIKE expression for you)

Queries constructed in this way return a special thing that looks like a tuple, and can be easily turned into a dict by calling _asdict() on it:

so to put it all together

output = [row._asdict() for row in DBSession.query(Client.id, Client.name)
                                            .filter(q in Client.name)]

or, if you really desperately need it to be a dict, you can use a dict comprehension:

output = {index: row._asdict() 
          for index, row 
          in enumerate(DBSession.query(Client.id, Client.name)
                                .filter(q in Client.name))}

Upvotes: 7

Related Questions