I'm often asked to export data stored in NDB models to csv. For that purpose, I usually ended up writing a model like this:
from google.appengine.ext import ndb
class Foo(ndb.Model):
monty = ndb.StringProperty()
python = ndb.StringProperty()
@property
@classmethod
def fieldnames(cls):
return ['monty', 'python']
and in the export module something along the lines of
# pseudocode ...
query = Foo.gql('where monty = :1', 'bunny')
data = [littlefoo._to_dict() for littlefoo in query]
fieldnames = Foo.fieldnames
with open('outfile.csv', 'w') as f:
writer = csv.DictWriter(f, fieldnames, dialect=dialect)
writer.writerows(data)
Note that the fieldnames method is needed for determining the order of the fields in the output csv. The problem with this approach is that for any model with a significant number of attributes, adding the fieldnames
method is ugly duplicate work. Ideally, I'd like to simply order the properties as I declare them, and retrieve them in the same order for fieldnames
. Is there a way to retrieve these properties in order? Foo._properties
is ordered alphabetically.
To my knowledge, this isn't possible without parsing the source for yourself. Luckily, with python's "batteries included" mentality, this isn't too hard. You can use
inspect
to get the source code and then you can useast
to parse the source and order things:Note that the approach here is almost general. I used the knowledge that
ndb.Model
s have a_properties
attribute, but that information could probably be gleaned fromdir
orinspect.getmembers
soorder_properties
could be modified so that it works completely generally.I'm just making this up, it's probably full of errors, but hopefully it starts you down the right path:
If using
google.appengine.ext.db
:db.Property
instances have an attribute calledcreation_counter
, which is incremented each time a new instance is created. So to get a list of properties sorted in declaration order, you can do something like:(where
Foo
is an instance ofdb.Model
)If using
google.appengine.ext.ndb
:Same thing, except the
ndb.Property
attribute is called_creation_counter
, so the equivalent code would be:(where
Foo
is an instance ofndb.Model
)