views:

195

answers:

5

Is there a good way to store a Python dictionary in the datastore? I want to do something like the following:

from google.appengine.ext import db

class Recipe(db.Model):
  name = db.StringProperty()
  style = db.StringProperty()
  yeast = db.StringProperty()
  hops = db.ListofDictionariesProperty()

Of course, that last line doesn't actually work. I need hops to be a list of key-value pairs, where the key is always a string and the value can be a string, int, or float, but I can't see anything in that would allow me to do that in the Property classes.

+2  A: 

You could pickle the dictionary and store it as a StringProperty.

John Paulett
+4  A: 

Serializing a dict with repr is a good way to do it. You can then reconstitute it with eval, or if you don't trust the data, a "safe eval".

An advantage of repr over pickling is that the data is readable in the database, even queryable in desperate cases.

Ned Batchelder
JSON is generally safer than eval.
S.Lott
I probably won't need to query that field, but I like that I have the option. Also, correct me if I'm wrong, but wouldn't using repr and using JSON have the same output in this case?
Dan Hook
Even with "safe eval," `repr` could be dangerous. Guido himself doubts that you could write such a function. See http://code.google.com/p/googleappengine/issues/detail?id=671. JSON is simpler than python, and I have more confidence that a JSON parser wouldn't be vulnerable in the way that `eval` can be.
lemnar
JSON is the better solution here, but for the record, you can write a safe eval that will deserialize dicts of primitive values safely.
Ned Batchelder
+2  A: 

I'm pretty sure there's no way to store a Python dictionary. But why not just place what you'd like in hops as a second model?

Also, as mentioned by John you could use pickle, but (and correct me if I'm wrong) store it as a Blob value instead.

Bartek
@Bartek, you can use the dumps and loads functions of the pickle module to work with strings:>>>cPickle.loads(cPickle.dumps({1:"one"})){1: 'one'}
Mark
+1  A: 

Your options are basically to use pickle, to use a db.Expando and make each key in the dict a separate property, or to have a StringListProperty of keys and one of values and zip() them back to a dict when reading.

Wooble
+3  A: 

You can use json

gnibbler
+1: JSON rules.
S.Lott