BadValueError:属性X是必需的
我在一个实体模型中添加了一个列表属性,这个模型已经有很多现存的实例。
class MyModel(db.Model):
new_property = db.ListProperty(item_type=str, default=None)
在把这个应用程序部署到实际环境后,刚开始运行得还不错,但过了一段时间就开始出现BadValueError错误,原因是在尝试从数据存储中获取记录时出错。
导致错误的代码其实就是直接调用数据存储的代码:
app_item = db.get(app_item_key)
我使用的是Python 2.7运行时的1.7.5版本。
有没有什么办法可以防止这个问题,或者至少能捕捉到这个错误,以便我能从数据存储中获取数据呢?
Traceback (most recent call last):
File "/base/data/home/apps/app/4-15.365909351579418812/app.py", line 1739, in app_get
app_item = db.get(app_item_key)
File "/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 1533, in get
return get_async(keys, **kwargs).get_result()
File "/python27_runtime/python27_lib/versions/1/google/appengine/api/apiproxy_stub_map.py", line 604, in get_result
return self.__get_result_hook(self)
File "/python27_runtime/python27_lib/versions/1/google/appengine/datastore/datastore_rpc.py", line 1459, in __get_hook
entities = rpc.user_data(entities)
File "/python27_runtime/python27_lib/versions/1/google/appengine/api/datastore.py", line 600, in local_extra_hook
return extra_hook(result)
File "/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 1503, in extra_hook
model = cls1.from_entity(entity)
File "/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 1438, in from_entity
return cls(None, _from_entity=entity, **entity_values)
File "/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 970, in __init__
prop.__set__(self, value)
File "/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 614, in __set__
value = self.validate(value)
File "/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 3460, in validate
value = super(ListProperty, self).validate(value)
File "/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 641, in validate
raise BadValueError('Property %s is required' % self.name)
BadValueError: Property new_property is required
给关注这个问题的人:
根据Aaron D的建议,把默认值改成一个空列表解决了这个问题,所以:
new_property = db.ListProperty(item_type=str, default=None)
应该改成:
new_property = db.ListProperty(item_type=str, default=[])
2 个回答
我很确定你这里的示例代码不是你实际使用的。我敢打赌你在新属性中设置了 required=True
。然后你在获取一个旧记录,而这个记录没有这个必需属性的值。只要去掉 'required=True',这些错误就会消失。如果你需要这个值是必需的,那么你需要在强制这个限制之前,先给这个字段添加一个默认值。
* 删除了一些关于 None
不是 ListProperty 有效默认值的完全无用的内容 *
所以我根据你提供的信息尝试重现这个情况,结果我找到了答案。我可以通过首先创建一个名为 new_property
的模型,这个模型的类型是 StringProperty,默认值是 None。然后用 put()
方法记录没有 new_property
值的记录,这样就会写入默认值 None,然后把模型定义中的 new_property
改为 ListProperty
,再去获取这个记录。我们会得到相同的错误追踪信息。请看下面的日志。
s~lightning-catfish> class MyModel(db.Model):
... pass
...
s~lightning-catfish> x = MyModel()
s~lightning-catfish> x.put()
datastore_types.Key.from_path(u'MyModel', 1001L, _app=u's~lightning-catfish')
s~lightning-catfish> class MyModel(db.Model):
... new_property = db.ListProperty(item_type=str,default=None)
...
s~lightning-catfish> y = db.get(x.key())
s~lightning-catfish> y
<MyModel object at 0x9e09dcc>
s~lightning-catfish> y.new_property
[]
s~lightning-catfish> new_property = db.StringProperty(defaul
KeyboardInterrupt
s~lightning-catfish> class MyModel(db.Model):
... new_property = db.StringProperty(default=None)
...
s~lightning-catfish> z = MyModel()
s~lightning-catfish> z.put()
datastore_types.Key.from_path(u'MyModel', 2001L, _app=u's~lightning-catfish')
s~lightning-catfish> class MyModel(db.Model):
... new_property = db.ListProperty(item_type=str,default=None)
...
s~lightning-catfish> a1 = db.get(z.key())
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/timh/google_appengine/google/appengine/ext/db/__init__.py", line 1533, in get
return get_async(keys, **kwargs).get_result()
File "/home/timh/google_appengine/google/appengine/api/apiproxy_stub_map.py", line 604, in get_result
return self.__get_result_hook(self)
File "/home/timh/google_appengine/google/appengine/datastore/datastore_rpc.py", line 1459, in __get_hook
entities = rpc.user_data(entities)
File "/home/timh/google_appengine/google/appengine/api/datastore.py", line 600, in local_extra_hook
return extra_hook(result)
File "/home/timh/google_appengine/google/appengine/ext/db/__init__.py", line 1503, in extra_hook
model = cls1.from_entity(entity)
File "/home/timh/google_appengine/google/appengine/ext/db/__init__.py", line 1438, in from_entity
return cls(None, _from_entity=entity, **entity_values)
File "/home/timh/google_appengine/google/appengine/ext/db/__init__.py", line 970, in __init__
prop.__set__(self, value)
File "/home/timh/google_appengine/google/appengine/ext/db/__init__.py", line 614, in __set__
value = self.validate(value)
File "/home/timh/google_appengine/google/appengine/ext/db/__init__.py", line 3460, in validate
value = super(ListProperty, self).validate(value)
File "/home/timh/google_appengine/google/appengine/ext/db/__init__.py", line 641, in validate
raise BadValueError('Property %s is required' % self.name)
BadValueError: Property new_property is required
s~lightning-catfish>
要修复这些数据,你需要在较低的层级访问它,并更改记录中存储的数据类型。
如果你需要,我有不使用模型来获取和存储实体的代码。
* 最后你应该尝试的事情 *
尝试使用以下代码或类似的代码来获取对象,而不使用模型。这样你可以得到底层的数据,包含类型等信息,以字典的形式返回。这将显示数据存储中的内容。
from google.appengine.api import datastore
from google.appengine.api import datastore_errors
def get_entities(keys):
rpc = datastore.GetRpcFromKwargs({})
keys, multiple = datastore.NormalizeAndTypeCheckKeys(keys)
entities = None
try:
entities = datastore.Get(keys, rpc=rpc)
except datastore_errors.EntityNotFoundError:
assert not multiple
return entities
x = get_entities([some_key])
在你提到的错误追踪中,查看Google App Engine的源代码中的__init__.py
文件,你会发现一个注释,在ListProperty的文档注释中(第3428行)写着:
注意,'required'的唯一允许值是True。
所以,即使你没有提供这个值,看起来第3442行的代码是自动设置这个值的:self._require_parameter(kwds, 'required', True)
如果你继续查看源代码(第3500行),你会看到ListProperty
的empty()
函数的定义:
def empty(self, value):
"""Is list property empty.
[] is not an empty value.
Returns:
True if value is None, else false.
"""
return value is None
我想到两个可能导致错误的问题,但我还没有通过测试来验证。
1) 如果因为某种原因,你在那个字段中已经有数据(也许你在重复使用new_property
这个名字?),而且它是空的,那么这很可能会导致你遇到的错误。我不太确定怎么解决这个问题,除了建议你使用一个独特的new_property
名称。我在评论中提到的帖子解释了如何“修复”数据。
2) 由于你已经有记录,你的代码试图用默认值None
来填充这些记录,这与empty()
的测试匹配,然后抛出异常。在这种情况下,如果你提供一个默认值[]
,应该就能正常工作。