#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 для массового загрузчика.