ndb 验证交易中的实体唯一性
ndb verify entity uniqueness in transaction
我一直在尝试创建具有 属性 的实体,它应该是唯一的或 None 类似于:
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
这就像一个魅力,直到我想在我用于 creating/updating 实体的 ndb 事务中执行此操作。喜欢:
@ndb.transactional
def create(the_thing):
validate_unique(the_thing)
the_thing.put()
但是 ndb 似乎只允许祖先查询,问题是我的模型没有 ancestor/parent。我可以执行以下操作来防止弹出此错误:
@ndb.non_transactional
def validate_unique(the_thing):
...
这感觉有点不合时宜,将某事声明为事务,然后让一个(重要的)部分在事务之外完成。我想知道这是要走的路还是有(更好的)选择。
还有一些关于为什么 ndb 只允许祖先查询的解释会很好。
由于您的唯一性检查涉及(全局)查询,这意味着它受制于 datastore's eventual consistency,这意味着它不会工作,因为查询可能无法检测到新创建的实体。
一个选择是切换到祖先查询,如果您的预期用途允许您使用这样的数据架构(或其他一些高度一致的方法)- 同一篇文章中有更多详细信息。
另一种选择是使用额外的数据作为临时缓存,您可以在其中存储 "a while" 的所有新创建实体的列表(让它们有足够的时间在全局中可见query) 除了查询结果中的那些之外,您还要在 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
我一直在尝试创建具有 属性 的实体,它应该是唯一的或 None 类似于:
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
这就像一个魅力,直到我想在我用于 creating/updating 实体的 ndb 事务中执行此操作。喜欢:
@ndb.transactional
def create(the_thing):
validate_unique(the_thing)
the_thing.put()
但是 ndb 似乎只允许祖先查询,问题是我的模型没有 ancestor/parent。我可以执行以下操作来防止弹出此错误:
@ndb.non_transactional
def validate_unique(the_thing):
...
这感觉有点不合时宜,将某事声明为事务,然后让一个(重要的)部分在事务之外完成。我想知道这是要走的路还是有(更好的)选择。
还有一些关于为什么 ndb 只允许祖先查询的解释会很好。
由于您的唯一性检查涉及(全局)查询,这意味着它受制于 datastore's eventual consistency,这意味着它不会工作,因为查询可能无法检测到新创建的实体。
一个选择是切换到祖先查询,如果您的预期用途允许您使用这样的数据架构(或其他一些高度一致的方法)- 同一篇文章中有更多详细信息。
另一种选择是使用额外的数据作为临时缓存,您可以在其中存储 "a while" 的所有新创建实体的列表(让它们有足够的时间在全局中可见query) 除了查询结果中的那些之外,您还要在 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