【问题标题】:ComputedProperty only updates on second put()ComputedProperty 仅在第二次 put() 时更新
【发布时间】:2015-05-15 18:17:22
【问题描述】:

我在StructuredProperty 中有一个ComputedProperty,它在首次创建对象时不会更新。

当我创建对象时,address_components_ascii 没有被保存。该字段在数据存储查看器中根本不可见。但是,如果我 get() 然后立即再次 put()(即使没有更改任何内容),ComputedProperty 将按预期工作。 address_components 字段工作正常。

我已尝试清除数据库,并删除整个数据库文件夹,但没有成功。

我在 Windows 7 上使用本地开发服务器。我没有在 GAE 上测试过。


代码如下:

class Item(ndb.Model):
    location = ndb.StructuredProperty(Location)

内部 Location 类:

class Location(ndb.Model):
    address_components = ndb.StringProperty(repeated=True)  # array of names of parent areas, from smallest to largest
    address_components_ascii = ndb.ComputedProperty(lambda self: [normalize(part) for part in self.address_components], repeated=True)

归一化函数

def normalize(s):
    return unicodedata.normalize('NFKD', s.decode("utf-8").lower()).encode('ASCII', 'ignore')

address_components 字段的示例:

[u'114B', u'Drottninggatan', u'Norrmalm', u'Stockholm', u'Stockholm', u'Stockholms l\xe4n', u'Sverige']

address_components_ascii 字段,在第二个put() 之后:

[u'114b', u'drottninggatan', u'norrmalm', u'stockholm', u'stockholm', u'stockholms lan', u'sverige']

【问题讨论】:

  • 计算属性的 ndb 文档中清楚地记录了这种行为 注意:ComputedProperties 不是在查询中计算的,而是在 put() 中计算的。如果您更新模型的架构以包含 ComputedProperty,则应记住通过加载现有实体并将其写入数据存储区来更新它们。有关更多信息,请参阅更新模型的架构
  • 不,这与旧的 ndb 对象无关。这是在新对象上的 put() 上,在 put() 时模型代码中的 ComputedProperty。
  • 好的,我接受。您现在遇到了事件顺序问题。正如您所发现的那样,事情并不完全正常。当与其他模型/属性一起使用时,您会发现 StructuredProperty 无法处理许多极端情况。例如 PolyModel 以及某些嵌套结构。在您的情况下,_prepare_for_put 可能会以您不期望的顺序在每个属性上被调用。 _prepare_for_put 在放置之前在每个属性上调用,在 ComputedProperties 的情况下,它是执行计算的触发器。
  • 所以在 put() 之前手动调用 _prepare_for_put() 应该修复它,而不需要 put-get-put 序列?这看起来确实是一些极端情况下的错误,因为它看起来并不难正确实现。
  • 值得一试,但这也取决于 StructuredProperty 的状态。我还注意到,声明属性的顺序没有得到遵守,因为属性存储在字典中(这意味着键的散列顺序)。您可能会发现 _prepare_for_put 是按该顺序调用的,这可能会对解决属性中复杂的依赖关系/关系产生影响。

标签: python google-app-engine google-cloud-datastore app-engine-ndb google-app-engine-python


【解决方案1】:

真正的问题似乎是 GAE 在StructuredProperty 上调用_prepare_for_put() 相对于对周围Model_pre_put_hook() 的调用的顺序。

我在Item._pre_put_hook() 中写信给address_components。我假设 GAE 在调用 Item 上的 _pre_put_hook() 之前计算了 StructuredPropertyComputedProperty。从 ComputedProperty 读取会导致重新计算其值。

我将此添加到_pre_put_hook() 的末尾:

# quick-fix: ComputedProperty not getting generated properly
# read from all ComputedProperties, to compute them again before put
_ = self.address_components_ascii

我将返回值保存到一个虚拟变量以避免 IDE 警告。

【讨论】:

  • 为什么不只是self.address_components_ascii 而不是_ = self.address_components_ascii?在这两种情况下都会触及属性。
  • I'm saving the return value to a dummy variable to avoid IDE warnings. 我的 IDE 删除了看起来无用的代码。这是写go的习惯。
【解决方案2】:

我刚刚在开发服务器上尝试了这段代码并且它工作正常。在 put 之前和之后都可以访问计算属性。

from google.appengine.ext import ndb

class TestLocation(ndb.Model):
  address = ndb.StringProperty(repeated=True)
  address_ascii = ndb.ComputedProperty(lambda self: [
    part.lower() for part in self.address], repeated=True)

class TestItem(ndb.Model):
  location = ndb.StructuredProperty(TestLocation)

item = TestItem(id='test', location=TestLocation(
  address=['Drottninggatan', 'Norrmalm']))
assert item.location.address_ascii == ['drottninggatan', 'norrmalm']
item.put()
assert TestItem.get_by_id('test').location.address_ascii == [
  'drottninggatan', 'norrmalm']

【讨论】:

  • 是的,如果您以任何方式访问 ComputedProperty,它就会被计算出来。也许我只是从 _pre_put_hook 中的所有 ComputedProperties 中读取?
  • 它适用于我,即使在放置之前没有访问它。无论如何,在每次 put 之前访问它看起来像 much 比做双重 put 更好的解决方案。
【解决方案3】:

这似乎是ndb 的限制。只需执行 put() 后跟 get() 和另一个 put() 即可。它速度较慢,但​​仅在第一次创建对象时才需要。

我添加了这个方法:

def double_put(self):
        return self.put().get().put()

这是put() 的直接替代品。

当我put() 一个新对象时,我调用MyObject.double_put() 而不是MyObject.put()

【讨论】:

  • 我想知道你是否可以在第一次放置之前手动计算计算属性,这样你就可以避免双重放置。
猜你喜欢
  • 2022-11-11
  • 1970-01-01
  • 1970-01-01
  • 2013-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-14
相关资源
最近更新 更多