Создание подкласса db.TextProperty для хранения python dict в формате JSON и установки кодировки по умолчанию на любую, кроме ASCII

#google-app-engine #unicode #utf-8 #google-cloud-datastore

#google-app-engine #unicode #utf-8 #google-облачное хранилище данных

Вопрос:

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

Однако некоторые из моих значений 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 для разработчиков.

Обратная трассировка (последний вызов last): File «/base/data/home/apps/delpythian/1.350065314722833389/core/handlers/ResetHandler.py «, строка 68, в _res_one возвращает res_one.put() Файл «/base/python_runtime/python_lib/versions/1/google / appengine/ext/db/init.py», строка 984, в файле put return datastore.Put(self._entity, config=config) «/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore.py «, строка 455, в Put возвращает _GetConnection().async_put(config, entities, extra_hook).get_result() Файл «/base/python_runtime/python_lib/versions/1/google/appengine/datastore/datastore_rpc.py «, строка 1219, в async_put для pbs в pbsgen: File «/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 возвращает объект._ToPb() Файл «/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore.py «, строка 841, в _ToPb properties = datastore_types. 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 () и следовать правилу «Декодировать рано, везде юникод, кодировать поздно».

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

Комментарии:

1. Пожалуйста, добавьте примеры кода и трассировку стека. В частности, я хотел бы подтвердить, что ошибка UnicodeError возникает при попытке сохранить TextProperty, а не ранее, когда вы пытаетесь сериализовать свой словарь Python в JSON.

2. Обновленный вопрос, добавлен код и трассировка стека.

Ответ №1:

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

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

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

У меня это работает как в dev, так и в prod.

Комментарии:

1. Здравствуйте и спасибо, я не думал о том, чтобы посмотреть на метод dumps (). Тем не менее, я все еще получаю ту же трассировку стека в prod. Отлично работает на dev. Это сбивает с толку!

Ответ №2:

Смотрим на строку: PackString pbvalue.set_stringvalue(юникод(значение).encode(‘utf-8’)) Ошибка UnicodeDecodeError: ‘ascii’

похоже, что appengine ожидает, что все строковые значения будут в юникоде. вызов 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 (по умолчанию), и именно поэтому unicode не может ее обработать.

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

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

и все должно быть в порядке.

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