ndb验证事务

时间:2016-03-31 09:24:52

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

我一直在尝试使用属性创建实体,该属性应该是唯一的或类似于:

class Thing(ndb.Model):
  something = ndb.StringProperty()
  unique_value = ndb.StringProperty()

由于ndb无法指定属性应该是唯一的,因此我自然会手动执行此操作:

def validate_unique(the_thing):
  if the_thing.unique_value and Thing.query(Thing.unique_value == the_thing.unique_value).get():
      raise NotUniqueException 

这就像一个魅力,直到我想在我用于创建/更新实体的ndb事务中执行此操作。像:

@ndb.transactional
def create(the_thing):
  validate_unique(the_thing)
  the_thing.put() 

然而ndb似乎只允许祖先查询,问题是我的模型没有祖先/父。我可以执行以下操作来防止此错误弹出:

@ndb.non_transactional
def validate_unique(the_thing):
  ...

这感觉有点不合适,宣布某事成为交易,然后在交易之外完成一个(重要)部分。我想知道这是否可行,或者是否有更好的选择。

关于为什么ndb只允许祖先查询的一些解释也不错。

1 个答案:

答案 0 :(得分:1)

由于您的唯一性检查涉及(全局)查询,因此它意味着它受datastore's eventual consistency的约束,这意味着它不会起作用,因为查询可能无法检测到新创建的实体。

如果您的预期用法允许您使用此类数据架构(或其他一些强烈一致的方法),则可以选择切换到祖先查询 - 同一篇文章中的更多详细信息。

另一种选择是使用额外的数据作为临时缓存,在其中存储所有新创建的实体的列表,用于"一段时间" (除了查询结果中的那些内容之外,您还可以在validate_unique()中查看全局查询中有足够的时间)。这将允许您在事务外部进行查询,并且只有在唯一性仍然存在的情况下才能输入事务,但最终结果是在事务内部手动检查缓存(即事务中没有查询)。

存在第三个选项(以一些额外的存储消耗作为价格),基于数据存储区对具有相同父级(或根本没有父级)的特定实体模型的唯一实体ID的强制执行。你可以有这样的模型:

class Unique(ndb.Model):  # will use the unique values as specified entity IDs!
    something = ndb.BooleanProperty(default=False)
您正在使用的

(该示例使用唯一父键,允许将模型重新用于具有唯一值的多个属性,如果您不需要,可以完全删除父级):

@ndb.transactional
def create(the_thing):
    if the_thing.unique_value:
        parent_key = get_unique_parent_key()
        exists = Unique.get_by_id(the_thing.unique_value, parent=parent_key)
        if exists:
            raise NotUniqueException
        Unique(id=the_thing.unique_value, parent=parent_key).put()
    the_thing.put()


def get_unique_parent_key():

    parent_id = 'the_thing_unique_value'
    parent_key = memcache.get(parent_id)
    if not parent_key:
        parent = Unique.get_by_id(parent_id)
        if not parent:
            parent = Unique(id=parent_id)
            parent.put()
        parent_key = parent.key
        memcache.set(parent_id, parent_key)
    return parent_key