python - how - Serialización JSON de los modelos de Google App Engine
ndb properties (13)
Como se menciona en , to_dict funciona muy bien. Aquí está mi código que estoy usando.
foos = query.fetch(10)
prepJson = []
for f in foos:
prepJson.append(db.to_dict(f))
myJson = json.dumps(prepJson))
He estado buscando durante bastante tiempo sin éxito. Mi proyecto no usa Django, ¿hay alguna forma sencilla de serializar los modelos de App Engine (google.appengine.ext.db.Model) en JSON o necesito escribir mi propio serializador?
Modelo:
class Photo(db.Model):
filename = db.StringProperty()
title = db.StringProperty()
description = db.StringProperty(multiline=True)
date_taken = db.DateTimeProperty()
date_uploaded = db.DateTimeProperty(auto_now_add=True)
album = db.ReferenceProperty(Album, collection_name=''photo'')
En la última versión (1.5.2) del SDK de App Engine, se introdujo en db.py
una función to_dict()
que convierte las instancias del modelo a diccionarios. Ver las notas de la versión .
No hay referencias a esta función en la documentación hasta el momento, pero lo he probado yo mismo y funciona como se esperaba.
Esta es la solución más simple que encontré. Requiere solo 3 líneas de códigos.
Simplemente agregue un método a su modelo para devolver un diccionario:
class DictModel(db.Model):
def to_dict(self):
return dict([(p, unicode(getattr(self, p))) for p in self.properties()])
SimpleJSON ahora funciona correctamente:
class Photo(DictModel):
filename = db.StringProperty()
title = db.StringProperty()
description = db.StringProperty(multiline=True)
date_taken = db.DateTimeProperty()
date_uploaded = db.DateTimeProperty(auto_now_add=True)
album = db.ReferenceProperty(Album, collection_name=''photo'')
from django.utils import simplejson
from google.appengine.ext import webapp
class PhotoHandler(webapp.RequestHandler):
def get(self):
photos = Photo.all()
self.response.out.write(simplejson.dumps([p.to_dict() for p in photos]))
Hay un método, "Model.properties ()", definido para todas las clases de modelos. Devuelve el dict que buscas.
from django.utils import simplejson
class Photo(db.Model):
# ...
my_photo = Photo(...)
simplejson.dumps(my_photo.properties())
Ver propiedades del modelo en los documentos.
He extendido la clase JSON Encoder escrita por share para admitir:
- Propiedades de los resultados de la consulta (por ejemplo, car.owner_set)
- ReferenceProperty: conviértalo recursivamente en JSON
Propiedades de filtrado: solo las propiedades con un nombre
verbose_name
se codificarán en JSONclass DBModelJSONEncoder(json.JSONEncoder): """Encodes a db.Model into JSON""" def default(self, obj): if (isinstance(obj, db.Query)): # It''s a reference query (holding several model instances) return [self.default(item) for item in obj] elif (isinstance(obj, db.Model)): # Only properties with a verbose name will be displayed in the JSON output properties = obj.properties() filtered_properties = filter(lambda p: properties[p].verbose_name != None, properties) # Turn each property of the DB model into a JSON-serializeable entity json_dict = dict([( p, getattr(obj, p) if (not isinstance(getattr(obj, p), db.Model)) else self.default(getattr(obj, p)) # A referenced model property ) for p in filtered_properties]) json_dict[''id''] = obj.key().id() # Add the model instance''s ID (optional - delete this if you do not use it) return json_dict else: # Use original JSON encoding return json.JSONEncoder.default(self, obj)
Incluso si no está utilizando django como marco, esas bibliotecas todavía están disponibles para su uso.
from django.core import serializers
data = serializers.serialize("xml", Photo.objects.all())
La respuesta de Mtgred anterior funcionó maravillosamente para mí, la modifiqué ligeramente para que también pudiera obtener la clave para la entrada. No hay tan pocas líneas de código, pero me da la clave única:
class DictModel(db.Model):
def to_dict(self):
tempdict1 = dict([(p, unicode(getattr(self, p))) for p in self.properties()])
tempdict2 = {''key'':unicode(self.key())}
tempdict1.update(tempdict2)
return tempdict1
No necesita escribir su propio "analizador" (un analizador probablemente convertiría JSON en un objeto de Python), pero aún puede serializar su objeto Python usted mismo.
Usando simplejson :
import simplejson as json
serialized = json.dumps({
''filename'': self.filename,
''title'': self.title,
''date_taken'': date_taken.isoformat(),
# etc.
})
Para casos simples, me gusta el enfoque defendido here al final del artículo:
# after obtaining a list of entities in some way, e.g.:
user = users.get_current_user().email().lower();
col = models.Entity.gql(''WHERE user=:1'',user).fetch(300, 0)
# ...you can make a json serialization of name/key pairs as follows:
json = simplejson.dumps(col, default=lambda o: {o.name :str(o.key())})
El artículo también contiene, en el otro extremo del espectro, una compleja clase de serializador que enriquece los de Django (y requiere _meta
- no estoy seguro de por qué está obteniendo errores sobre _meta faltante, quizás el error descrito here ) con la capacidad de serializar propiedades / métodos computados La mayoría de las veces, las necesidades de serialización se encuentran en algún punto intermedio, y para aquellas, un enfoque introspectivo como el de @David Wilson puede ser preferible.
Para serializar modelos, agregue un codificador json personalizado como en el siguiente pitón:
import datetime
from google.appengine.api import users
from google.appengine.ext import db
from django.utils import simplejson
class jsonEncoder(simplejson.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj, db.Model):
return dict((p, getattr(obj, p))
for p in obj.properties())
elif isinstance(obj, users.User):
return obj.email()
else:
return simplejson.JSONEncoder.default(self, obj)
# use the encoder as:
simplejson.dumps(model, cls=jsonEncoder)
Esto codificará:
- una fecha como una cadena isoformat ( según esta sugerencia ),
- un modelo como un dict de sus propiedades,
- un usuario como su correo electrónico.
Para decodificar la fecha puedes usar este javascript:
function decodeJsonDate(s){
return new Date( s.slice(0,19).replace(''T'','' '') + '' GMT'' );
} // Note that this function truncates milliseconds.
Nota: Gracias al usuario pydave que editó este código para hacerlo más legible. Originalmente había usado las expresiones if / else de python para expresar jsonEncoder
en menos líneas de la siguiente manera: (He agregado algunos comentarios y usé google.appengine.ext.db.to_dict
, para hacerlo más claro que el original).
class jsonEncoder(simplejson.JSONEncoder):
def default(self, obj):
isa=lambda x: isinstance(obj, x) # isa(<type>)==True if obj is of type <type>
return obj.isoformat() if isa(datetime.datetime) else /
db.to_dict(obj) if isa(db.Model) else /
obj.email() if isa(users.User) else /
simplejson.JSONEncoder.default(self, obj)
Para serializar una instancia de Datastore Model no puede usar json.dumps (no lo han probado pero Lorenzo lo señaló). Quizás en el futuro, lo siguiente funcione.
http://docs.python.org/2/library/json.html
import json
string = json.dumps([''foo'', {''bar'': (''baz'', None, 1.0, 2)}])
object = json.loads(self.request.body)
Se puede usar una función recursiva simple para convertir una entidad (y cualquier referencia) en un diccionario anidado que se puede pasar a simplejson
:
import datetime
import time
SIMPLE_TYPES = (int, long, float, bool, dict, basestring, list)
def to_dict(model):
output = {}
for key, prop in model.properties().iteritems():
value = getattr(model, key)
if value is None or isinstance(value, SIMPLE_TYPES):
output[key] = value
elif isinstance(value, datetime.date):
# Convert date/datetime to MILLISECONDS-since-epoch (JS "new Date()").
ms = time.mktime(value.utctimetuple()) * 1000
ms += getattr(value, ''microseconds'', 0) / 1000
output[key] = int(ms)
elif isinstance(value, db.GeoPt):
output[key] = {''lat'': value.lat, ''lon'': value.lon}
elif isinstance(value, db.Model):
output[key] = to_dict(value)
else:
raise ValueError(''cannot encode '' + repr(prop))
return output
Si usa app-engine-patch , declarará automáticamente el atributo _meta
por usted, y luego podrá usar django.core.serializers
como lo haría normalmente en los modelos de django (como en el código de trineo).
App-engine-patch tiene otras características geniales, como la autenticación híbrida (cuentas django + google) y la parte administrativa de django.