我一直在尝试使用属性创建实体,该属性应该是唯一的或类似于:
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只允许祖先查询的一些解释也不错。
答案 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