Создание подкласса db.TextProperty для хранения python dict как JSON и установки кодировки по умолчанию для чего угодно, кроме ASCII - PullRequest
3 голосов
/ 29 апреля 2011

Используя Google App Engine (python SDK), я создал собственный JSONProperty () в качестве подкласса db.TextProperty (). Моя цель - хранить на лету в виде JSON информацию о питоне и легко ее извлекать. Я следовал различным примерам, найденным через Google, и настроить собственный класс и методы Property довольно просто.

Однако некоторые из моих значений dict (строк) кодируются в utf-8. При сохранении модели в хранилище данных я получаю страшную ошибку Unicode (для текстового свойства хранилища данных по умолчанию используется кодировка ASCII). Подклассы db.BlobProperty не решили проблему.

По сути, мой код выполняет следующие действия: сохраняет сущности ресурсов в хранилище данных (с URL-адресами в качестве StringProperty и полезными нагрузками POST / GET, хранящимися в dict как JSONProperty), извлекает их позже (код не включен). Я решил не использовать pickle для хранения полезных нагрузок, потому что я фанат JSON и не могу хранить объекты.

Пользовательский JSONProperty:

class JSONProperty(db.TextProperty):
    def get_value_for_datastore(self, model_instance):
        value = super(JSONProperty, self).get_value_for_datastore(model_instance)
        return json.dumps(value)

    def make_value_from_datastore(self, value):
        if value is None:
            return {}
        if isinstance(value, basestring):
            return json.loads(value)
        return value

Помещение модели в хранилище данных:

res = Resource()
res.init_payloads()
res.url = "http://www.somesite.com/someform/"
res.param = { 'name': "SomeField", 'default': u"éàôfoobarç" }
res.put()

Это вызовет UnicodeDecodeError, связанную с кодировкой ASCII. Возможно, стоит отметить, что я получаю эту ошибку (каждый раз) только на производственном сервере. Я использую Python 2.5.2 на Dev.

Traceback (последний вызов был последним): Файл "/base/data/home/apps/delpythian/1.350065314722833389/core/handlers/ResetHandler.py", строка 68, в _res_one return res_one.put () Файл "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/init.py", строка 984, на месте return datastore.Put (self._entity, config = config) Файл "/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore.py", строка 455, в Put return _GetConnection (). async_put (config, entity, extra_hook) .get_result () Файл "/base/python_runtime/python_lib/versions/1/google/appengine/datastore/datastore_rpc.py", строка 1219, в async_put для pbs в pbsgen: Файл "/base/python_runtime/python_lib/versions/1/google/appengine/datastore/datastore_rpc.py", строка 1070, в __generate_pb_lists pb = value_to_pb (значение) Файл "/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore.py", строка 239, в entity_to_pb вернуть entity._ToPb () Файл "/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore.py", строка 841, в _ToPb properties = datastore_types.ToPropertyPb (имя, значения) Файл "/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore_types.py", строка 1672, в ToPropertyPb pbvalue = pack_prop (name, v, pb.mutable_value ()) Файл "/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore_types.py", строка 1485, в PackString pbvalue.set_stringvalue (Юникода (значение) .encode ( 'UTF-8')) UnicodeDecodeError: кодек «ascii» не может декодировать байт 0xc3 в позиции 32: порядковый номер не в диапазоне (128)

Мой вопрос заключается в следующем: есть ли способ создать подкласс класса db.TextProperty () и установить / применить пользовательскую кодировку? Или я что-то не так делаю? Я стараюсь избегать использования str () и придерживаюсь «Декодировать рано, везде Юникод, кодировать поздно» .

Обновление: добавлены код и трассировка стека.

Ответы [ 2 ]

2 голосов
/ 29 апреля 2011

Вот минимальный пример перемещения строки юникода из словаря в сериализованную строку JSON в TextProperty:

class Thing(db.Model):
  json = db.TextProperty()

class MainHandler(webapp.RequestHandler):
  def get(self):
    data = {'word': u"r\xe9sum\xe9"}
    json = simplejson.dumps(data, ensure_ascii=False)
    Thing(json=json).put()

Это работает для меня как в dev, так и в prod.

1 голос
/ 15 мая 2011

Глядя на строку: PackString pbvalue.set_stringvalue (unicode (значение) .encode ('utf-8')) UnicodeDecodeError: 'ascii'

кажется, что appengine ожидает, что все строковые значения будут в Unicode.вызов unicode (значение) не определяет кодировку, поэтому, вероятно, по умолчанию будет ascii, если значение уже не является Unicode, например:

>>> u = u"ąęćźż"
>>> s = u.encode('utf-8')
>>> unicode(u) # fine
>>> unicode(s, 'utf-8') # fine
>>> unicode(s) # blows up (try's ascii) (on my interpreter)

json.dumps будет кодировать строку utf-8 (по умолчанию)и именно поэтому юникод не может с этим справиться.

попробуйте это:

>>> return unicode(json.dumps(...), 'utf-8')

и с вами все будет в порядке.

Что касается того, почему appengine взорвался, а ваш переводчикхорошо, я предполагаю, что будут некоторые локальные настройки, docstring для unicode говорит, что по умолчанию используется текущая кодировка по умолчанию, которая, по-видимому, является utf-8 для вас и ascii для gae.

...