【发布时间】:2018-03-03 17:18:28
【问题描述】:
所以,我已经将我讨厌的问题缩小到这个......
class TestModel(ndb.Model):
json1 = ndb.JsonProperty(default={})
entity1 = TestModel()
entity1.json1['val1'] = 'added via entity1'
entity2 = TestModel()
entity2.json1['val2'] = 'added via entity2'
logging.warn('entity2.json1 = {}'.format(entity2.json1))
在日志中,我看到了这个:
... entity2.json1 = {'val2': 'added via entity2', 'val1': 'added via entity1'}
令人惊讶且极其危险的是,我发现在第一个实例中设置的值 entity1 已泄漏到第二个实例 entity2 中。
我期望TestModel 的第二个实例化为我提供一个“干净”的实例是不合理的,特别是因为我有default={} 用于JsonProperty?我应该做一些我没有做的事情吗?或者这可能是 ndb 的一个错误?
更新:到目前为止我最好的解决方法:总是使用TestModel(json1={})。但我想我担心如果我们的一位开发人员忘记这样做,我们可能会将一位客户的数据泄露给另一位客户。
更新:似乎已经向 Google 报告了错误。 35898756 表明这种(错误)行为可能在请求之间发生。 3年前开业;仍在等待修复。
【问题讨论】:
-
这是一个偷偷摸摸的 - 我只是在编写一些碰巧实例化和对象多次具有相似属性的测试时才注意到。这不是错误,它与任何其他具有可变默认值的函数 def 相同。 dict 似乎在请求之间自行重置,不确定这是 ndb 还是描述符创建方式的某些方面。我认为唯一的补救措施是保持警惕,或者可能是自定义 linter 规则。
-
谢谢@snakecharmerb,很高兴听到我不是唯一一个。我是一个长期的 GAE 用户,直到最近才开始使用它。在过去,我必须一直在我的 JsonProperty 中设置相同的值。我没有意识到我正在替换值,而不是添加它们。
-
@snakecharmerb,我想了更多关于你的评论,它类似于函数
defs 中的 args 的默认值。这可能是它的方式,但不是必须的。理想情况下,当创建一个新实例时,ndb 应该分配copy.deepcopy(default)。 -
这很方便,但我仍然倾向于认为它不是一般情况下的错误。另一方面,我在远程 shell 中使用 JsonProperty 创建了一个模型,并在两个不同的命名空间中使用不同的键保存;第二个命名空间中的实体包含保存在第一个命名空间中的模型的键。我认为有一个更强(尽管不是 100%)的论点认为这是一个错误,因为跨命名空间泄漏的数据会破坏多租户合同。但如果谷歌不修复它,我猜你将不得不继承 JsonProperty 并自己实现 deepcopy :-)
-
基本上无论你在哪里使用默认参数和可变数据结构,如字典或列表(例如
default={}),你都会遇到麻烦。不仅与ndb。见docs.quantifiedcode.com/python-anti-patterns/correctness/…
标签: google-app-engine app-engine-ndb google-app-engine-python