python - with - ¿Cómo serializar objetos db.Model a json?
typeerror is not json serializable (5)
Como no pude encontrar una solución adecuada, escribí la mía, que no es exactamente un serializador JSON, sino un serializador Javascript.
from google.appengine.ext import db
from google.appengine.api.datastore_types import *
def dumpStr(obj):
return "''" + obj + "''"
def dumps(obj):
if isinstance(obj, str):
return dumpStr(obj)
elif obj == None:
return None
elif isinstance(obj, list):
items = [];
for item in obj:
items.append(dumps(item))
return ''['' + '',''.join(items) + '']''
elif isinstance(obj, datetime.datetime):
return "new Date(''%s'')" % obj.ctime()
properties = [];
for property in dir(obj):
if property[0] != ''_'':
value = obj.__getattribute__(property)
valueClass = str(value.__class__)
if not((''function'' in valueClass) or (''built'' in valueClass) or (''method'' in valueClass)):
value = dumps(value)
if value != None:
properties.append("''" + property + "'':" + value)
if len(properties) == 0:
return str(obj)
else:
return ''{'' + '',''.join(properties) + ''}''
Cuando usas
from django.utils import simplejson
en objetos de tipos que se derivan de db.Model
lanza excepciones. ¿Cómo sortear esto?
El ejemplo proporcionado por Jader Dias funciona bien para mi preocupación después de algunas modificaciones. Elimine el método de codificación ya que contiene una referencia circular. La clase ajustada debe verse como:
import datetime
import time
from google.appengine.api import users
from google.appengine.ext import db
from django.utils import simplejson
class GqlEncoder(simplejson.JSONEncoder):
"""Extends JSONEncoder to add support for GQL results and properties.
Adds support to simplejson JSONEncoders for GQL results and properties by
overriding JSONEncoder''s default method.
"""
# TODO Improve coverage for all of App Engine''s Property types.
def default(self, obj):
"""Tests the input object, obj, to encode as JSON."""
if hasattr(obj, ''__json__''):
return getattr(obj, ''__json__'')()
if isinstance(obj, db.GqlQuery):
return list(obj)
elif isinstance(obj, db.Model):
properties = obj.properties().items()
output = {}
for field, value in properties:
output[field] = getattr(obj, field)
return output
elif isinstance(obj, datetime.datetime):
output = {}
fields = [''day'', ''hour'', ''microsecond'', ''minute'', ''month'', ''second'', ''year'']
methods = [''ctime'', ''isocalendar'', ''isoformat'', ''isoweekday'', ''timetuple'']
for field in fields:
output[field] = getattr(obj, field)
for method in methods:
output[method] = getattr(obj, method)()
output[''epoch''] = time.mktime(obj.timetuple())
return output
elif isinstance(obj, datetime.date):
output = {}
fields = [''year'', ''month'', ''day'']
methods = [''ctime'', ''isocalendar'', ''isoformat'', ''isoweekday'', ''timetuple'']
for field in fields:
output[field] = getattr(obj, field)
for method in methods:
output[method] = getattr(obj, method)()
output[''epoch''] = time.mktime(obj.timetuple())
return output
elif isinstance(obj, time.struct_time):
return list(obj)
elif isinstance(obj, users.User):
output = {}
methods = [''nickname'', ''email'', ''auth_domain'']
for method in methods:
output[method] = getattr(obj, method)()
return output
return simplejson.JSONEncoder.default(self, obj)
Como he guardado esta clase en un archivo llamado utils.py y cuando sea apropiado, lo importo usando
import utils
Luego solo llamo a utils.GqlEncoder (). Encode (resultados), por ejemplo:
query = User.all()
results = query.fetch(10)
self.response.headers[''Content-Type''] = "text/plain" # Alt. application/json
self.response.out.write( utils.GqlEncoder().encode(results) )
El resultado debería ser similar a este (he agregado algunas líneas de información para que sea más fácil de leer):
[
{"date": {"ctime": "Tue Feb 23 10:41:21 2010", "hour": 10, "isoweekday": 2, "month": 2,
"second": 21, "microsecond": 495535, "isocalendar": [2010, 8, 2], "timetuple": [2010, 2, 23, 10, 41, 21, 1, 54, -1],
"year": 2010, "epoch": 1266921681.0, "isoformat": "2010-02-23T10:41:21.495535", "day": 23, "minute": 41},
"claimed_id": "https:////www.google.com//accounts//o8//id?id=abcdefghijklmnopqrstuvxyz",
"display_name": "Alfred E Neumann",
"email": null,
"full_name": "Alfred E Neumann"
},
{"date": {"ctime": "Tue Feb 23 11:00:54 2010", "hour": 11, "isoweekday": 2, "month": 2,
"second": 54, "microsecond": 805261, "isocalendar": [2010, 8, 2], "timetuple": [2010, 2, 23, 11, 0, 54, 1, 54, -1],
"year": 2010, "epoch": 1266922854.0, "isoformat": "2010-02-23T11:00:54.805261", "day": 23, "minute": 0},
"claimed_id": "http:////openid.domain.net//john",
"display_name": "",
"email": "[email protected]",
"full_name": "John Parnefjord"
}
]
Ok, mi python no es genial, así que cualquier ayuda sería apreciada. No necesitas escribir un analizador. Esta es la solución:
agrega esta clase de utilidades http://code.google.com/p/google-app-engine-samples/source/browse/trunk/geochat/json.py?r=55
import datetime
import time
from google.appengine.api import users
from google.appengine.ext import db
#this is a mod on the orinal file for some reason it includes its own simplejson files i have ref django!
from django.utils import simplejson
class GqlEncoder(simplejson.JSONEncoder):
"""Extends JSONEncoder to add support for GQL results and properties.
Adds support to simplejson JSONEncoders for GQL results and properties by
overriding JSONEncoder''s default method.
"""
# TODO Improve coverage for all of App Engine''s Property types.
def default(self, obj):
"""Tests the input object, obj, to encode as JSON."""
if hasattr(obj, ''__json__''):
return getattr(obj, ''__json__'')()
if isinstance(obj, db.GqlQuery):
return list(obj)
elif isinstance(obj, db.Model):
properties = obj.properties().items()
output = {}
for field, value in properties:
output[field] = getattr(obj, field)
return output
elif isinstance(obj, datetime.datetime):
output = {}
fields = [''day'', ''hour'', ''microsecond'', ''minute'', ''month'', ''second'',
''year'']
methods = [''ctime'', ''isocalendar'', ''isoformat'', ''isoweekday'',
''timetuple'']
for field in fields:
output[field] = getattr(obj, field)
for method in methods:
output[method] = getattr(obj, method)()
output[''epoch''] = time.mktime(obj.timetuple())
return output
elif isinstance(obj, time.struct_time):
return list(obj)
elif isinstance(obj, users.User):
output = {}
methods = [''nickname'', ''email'', ''auth_domain'']
for method in methods:
output[method] = getattr(obj, method)()
return output
return simplejson.JSONEncoder.default(self, obj)
def encode(input):
"""Encode an input GQL object as JSON
Args:
input: A GQL object or DB property.
Returns:
A JSON string based on the input object.
Raises:
TypeError: Typically occurs when an input object contains an unsupported
type.
"""
return GqlEncoder().encode(input)
guardar como json.py
USAR
import cgi
import os
import json
from google.appengine.ext.webapp import template
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
class Greeting(db.Model):
author = db.UserProperty()
content = db.StringProperty(multiline=True)
date = db.DateTimeProperty(auto_now_add=True)
class MainPage(webapp.RequestHandler):
def get(self):
greetings_query = Greeting.all().order(''-date'')
greetings = greetings_query.fetch(5)
if users.get_current_user():
url = users.create_logout_url(self.request.uri)
url_linktext = ''Logout''
else:
url = users.create_login_url(self.request.uri)
url_linktext = ''Login''
template_values = {
''greetings'': greetings,
''url'': url,
''url_linktext'': url_linktext,
}
path = os.path.join(os.path.dirname(__file__), ''index.html'')
self.response.out.write(template.render(path, template_values))
class Guestbook(webapp.RequestHandler):
def post(self):
greeting = Greeting()
if users.get_current_user():
greeting.author = users.get_current_user()
greeting.content = self.request.get(''content'')
greeting.put()
self.redirect(''/'')
#here i return my json feed - simple implementaion for example
class FeedHandler(webapp.RequestHandler):
def get(self):
"""Retrieve a feed"""
user = None
greetings_query = Greeting.all().order(''-date'')
rs= greetings_query.fetch(5)
#this is the part that calls the encoder - dosnt cause an exception
data = json.encode(rs)
#roll out to browser -might need to check my headers etc
self.response.headers[''Content-Type''] = ''application/json; charset=utf-8''
self.response.out.write(data)
application = webapp.WSGIApplication(
[
(''/'', MainPage),
(''/sign'',Guestbook),
(''/feed'',FeedHandler),
], debug=True
)
def main():
run_wsgi_app(application)
if __name__ == "__main__":
main()
Esta es la respuesta del navegador:
[{"content": "", "date": {"ctime": "Sat Jan 23 02:40:22 2010", "hour": 2, "isoweekday": 6, "month": 1, "second ": 22," microsegundo ": 434000," isocalendar ": [2010, 3, 6]," timetuple ": [2010, 1, 23, 2, 40, 22, 5, 23, -1]," año " : 2010, "epoch": 1264214422.0, "isoformat": "2010-01-23T02: 40: 22.434000", "day": 23, "minute": 40}, "author": {"nickname": "test @ example.com "," email ":" [email protected] "," auth_domain ":" gmail.com "}}, {" content ":",, date ": {" ctime ":" Sat Jan 23 01:12:43 2010 "," hora ": 1," isoweekday ": 6," mes ": 1," segundo ": 43," microsegundo ": 972000," isocalendar ": [2010, 3, 6], "timetuple": [2010, 1, 23, 1, 12, 43, 5, 23, -1], "año": 2010, "época": 1264209163.0, "isoformato": "2010-01-23T01: 12: 43.972000 "," día ": 23," minuto ": 12}," autor ": {" apodo ":" [email protected] "," correo electrónico ":" [email protected] "," auth_domain ":" gmail.com "}}, {" content ":" test "," date ": {" ctime ":" Fri Jan 22 22:32:13 2010 "," hour ": 22," isoweekday ": 5," mes ": 1," segundo ": 13," microsegundo ": 659000," es ocalendar ": [2010, 3, 5]," timetuple ": [2010, 1, 22, 22, 32, 13, 4, 22, -1]," año ": 2010," epoch ": 1264199533.0," isoformato ":" 2010-01-22T22: 32: 13.659000 "," day ": 22," minute ": 32}," author ": {" nickname ":" [email protected] "," email ":" test @ example.com "," auth_domain ":" gmail.com "}}, {" content ":", "date": {"ctime": "Fri Jan 22 22:29:49 2010", "hour" : 22, "isoweekday": 5, "month": 1, "second": 49, "microsecond": 358000, "isocalendar": [2010, 3, 5], "timetuple": [2010, 1, 22, 22, 29, 49, 4, 22, -1], "año": 2010, "época": 1264199389.0, "isoformato": "2010-01-22T22: 29: 49.358000", "día": 22, "minuto ": 29}," author ": {" nickname ":" [email protected] "," email ":" [email protected] "," auth_domain ":" gmail.com "}}, {" content " : "ah, funciona! / r / n", "fecha": {"ctime": "Viernes 22 de enero 22:29:22 2010", "hora": 22, "isoweekday": 5, "mes": 1 , "second": 22, "microsecond": 995000, "isocalendar": [2010, 3, 5], "timetuple": [2010, 1, 22, 22, 29, 22, 4, 22, -1], "año": 2010, "época": 1264199362.0, "isoformato": " 2010-01-22T22: 29: 22.995000 "," day ": 22," minute ": 29}," author ": {" nickname ":" [email protected] "," email ":" test @ example. com "," auth_domain ":" gmail.com "}}]
Por lo que puedo entender, y soy nuevo en Python, con el motor de aplicación de google el trabajo es serializar el objeto modelo a un objeto de dictado de python y luego usar json simple para volcarlo como una cadena json. Esto no tiene sentido para mí - tal vez alguien tenga el conocimiento de serializar a un ditionary (pickel?) ¡Cualquier ayuda en esto sería AYUDA! No impresionó que el motor de aplicaciones de Google no tenga una solución integrada para esto.
json no se puede usar para serializar nada más que tipos básicos, como dictados, listas, ints / longs y cadenas (esto no es exhaustivo). Por ejemplo, incluso estos comandos simples no funcionan:
import json
json.dumps(object())
Si desea serializar objetos django, debe consultar la documentación de django sobre la serialización , que utilizará sus propias bibliotecas, pero sí son compatibles con json.