Google AppEngine сообщает мне, что мой int не является int

#python #google-app-engine #google-cloud-datastore

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

Вопрос:

Соответствующая часть кода:

 pk = int(pk)                              
logging.info('pk: %r :: %s', pk, type(pk))
instance = models.Model.get_by_id(int(pk))       
  

Вывод из приведенного выше сообщения журнала

 pk: 757347 :: <type 'int'>
  

Трассировка стека:

 Traceback (most recent call last):
  File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/webapp/__init__.py", line 634, in __call__
    handler.get(*groups)
  File "/base/data/home/apps/<myapp>/<version>/scrape.py", line 61, in get
    instance = models.Model.get_by_id(int(pk))
  File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init__.py", line 1212, in get_by_id
    return get(keys[0], config=config)
  File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init__.py", line 1434, in get
    model = cls1.from_entity(entity)
  File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init__.py", line 1350, in from_entity
    instance = cls(None, _from_entity=True, **entity_values)
  File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init__.py", line 890, in __init__
    prop.__set__(self, value)
  File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init__.py", line 593, in __set__
    value = self.validate(value)
  File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init__.py", line 2967, in validate
    % (self.name, type(value).__name__))
BadValueError: Property pk must be an int or long, not a unicode
  

У кого-нибудь есть идея, не делаю ли я здесь что-то не так?

Примечание: удаление int из последней строки кода не имеет никакого значения (это была первая версия).

Кроме того, код работает без проблем на dev_appserver.py .

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

1. Я думаю, что проблема не в последней строке. Он находится в logging.info

2. К сожалению, проблема не в протоколировании. Вы уверены, что объект с идентификатором 757347 существует в хранилище данных..

3. @Abdul Kader: он должен вернуть None , если элемент не существует. Но я также попытался удалить объект, и это не имело никакого значения.

4. Также убедитесь, что вы не путаете id с pk. Похоже, что ваша модель имеет свойство с именем pk, но вы вызываете get_by_id(), которое получит объект с id = 757347, который может отличаться от объекта с pk = 757347.

5. @Saxon Druce: Для удобства работы с некоторыми другими системами у меня также есть pk свойство IntegerProperty. pk Свойство должно быть идентичным id используемому ключом. Если они не идентичны, то у меня совершенно другая проблема 😉

Ответ №1:

Есть ли у вашей модели свойство ‘pk’, которое теперь является IntegerProperty(), но ранее было StringProperty(), а объект с идентификатором 757347 был сохранен в старой версии модели?

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

1. Нет, но я загрузил данные с помощью bulkloader, так что есть большая вероятность, что они были загружены не как целое число, я проверю это и свяжусь с вами.

2. Извините за медленный ответ, я был довольно занят. Но … ваш ответ был точен. Удалить элементы было немного сложно (редактирование / сохранение обычно было невозможно), но это сработало. Большое вам спасибо за помощь.

Ответ №2:

Создайте пользовательский валидатор для вашего pk IntegerProperty.

Я думаю, что @saxon-druce имеет правильное представление о том, что происходит сбой.

Вы получаете объект из хранилища данных, а функция from_entity применяет данные из объекта к инициализатору для вашей db.Model.

вызов validate выполняется из google/appengine/ext/db/__init__.py

Из SDK

 class IntegerProperty(Property):
  """An integer property."""

  def validate(self, value):
    """Validate integer property.

    Returns:
      A valid value.

    Raises:
      BadValueError if value is not an integer or long instance.
    """
    value = super(IntegerProperty, self).validate(value)
    if value is None:
      return value





    if not isinstance(value, (int, long)) or isinstance(value, bool):
      raise BadValueError('Property %s must be an int or long, not a %s'
                          % (self.name, type(value).__name__))
    if value < -0x8000000000000000 or value > 0x7fffffffffffffff:
      raise BadValueError('Property %s must fit in 64 bits' % self.name)
    return value

  data_type = int
  

Создайте свой собственный тривиальный валидатор, который пытается проанализировать строку как int.
В конечном итоге вы, вероятно, захотите применить mapper ко всем этим объектам, чтобы привести их все к текущей схеме.

Валидатор вызывается внутри value = super(IntegerProperty, self).validate(value) , поэтому значение должно быть готово к использованию в качестве int в соответствующее время.

Пример валидатора

 def str_int_validator(value):
    if isinstance(value, basestring):
        if value.isdigit():
            return int(value)
        else:
            raise db.BadValueError("Property expected str or int got %r" % value)
    else:
        return value

class Foo(db.Model):

    pk = db.IntegerProperty(validator=str_int_validator)
  

Этот код не тестировался.

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

1. Вау, отличный ответ. Я скоро попробую 🙂

2. К сожалению, добавление пользовательского средства проверки не устранило проблему. Сейчас я пытаюсь удалить строки-нарушители, поскольку они, похоже, все равно повреждены

3. Проблема действительно была такой, как ее описал саксон-дрюс. Спасибо за ваш отличный ответ, но я должен отдать должное, где это уместно, saxon-druce был первым, и этот код не решил проблему для меня. По крайней мере, 1 от меня 🙂

Ответ №3:

У меня было такое же сообщение об ошибке после удаления всех элементов в одной сущности / таблице, а затем попытки загрузить новые значения через csv / bulkloader.

Решением было добавить следующую строку

 import_transform: transform.none_if_empty(int)
  

к определению свойства в файле yaml для массового загрузчика.