我正在尝试解决基于this的赛车问题,以防止重复的用户注册。因此,如果帐户存在或电子邮件已被使用,则不会创建任何实体。
@ndb.transactional
def get_or_insert2(account, email):
accountExists, emailExists = False, False
entity = Member.get_by_id(account)
if entity is not None:
accountExists = True
if Member.query(Member.email==email).fetch(1):
emailExists = True
if not accountExists and not emailExists:
entity = Member(id=account)
entity.put()
return (entity, accountExists, emailExists)
我的问题:
我收到一条错误消息:BadRequestError:在事务中只允许祖先查询。问题是什么?
代码是否正确?我的意思是,它能真正解决赛车问题吗?
感谢。
答案 0 :(得分:3)
Transactions处理实体组,您可以在跨群组事务中包含最多5个实体组。实体组由单个服务器(或组,已复制)处理,这意味着在检查数据或在实体组中执行祖先查询时,它能够具有一致的内部状态。
常规查询是全局的,具有最终一致性的索引。您不知道所有节点的所有更改何时都包含在索引中。您无法锁定整个数据存储区以获得事务的一致快照状态。如果您习惯使用查询的一致索引,这与常规RDBMS的关键区别。
对于1),问题在于您在事务中进行常规查询,这不会像上面解释的那样工作。 2)的答案然后变为否,查询无法解决赛车问题,你需要明确获取。
您需要一个会员,电子邮件和SSN模型。这是一个快速未经测试的例子,希望能让你前进:
class Member(ndb.Model):
email = ndb.KeyProperty()
ssn = ndb.KeyProperty()
# More user properties goes here...
class Email(ndb.Model):
member = ndb.KeyProperty()
class SSN(ndb.Model):
member = ndb.KeyProperty()
@ndb.tasklet
def get_or_insert2(account, email, ssn):
created = False
member_key = ndb.Key(Member, account)
email_key = ndb.Key(Email, email)
ssn_key = ndb.Key(SSN, ssn)
member_obj, email_obj, ssn_obj = yield ndb.get_multi_async([member_key, email_key, ssn_key])
if member_obj is None and email_obj is None and ssn_obj is None:
member_obj = Member(key=member_key, email=email_key, ssn=ssn_key))
email_obj = Email(key=email_key, member=member_key)
ssn_obj = SSN(key=ssn_key, member=member_key)
yield ndb.put_multi_async([member_obj, email_obj])
created = True
raise ndb.Return([created, member_obj, email_obj, ssn_obj])
outcome = ndb.transaction(lambda: get_or_insert2(account, email, ssn), xg=True)
我不确定它是否可以合并@ ndb.tasklet和@ ndb.transactional(xg = True)装饰器,如果是,那么只需要试一试。
如果您需要根据电子邮件或ssn查询用户,您可以将KeyProperties重命名为* _ref并制作类似
的内容@ndb.ComputedProperty
def email(self):
return self.email_ref.id()
虽然最终会出现比您预期更多的代码行,但它在概念上很简单直接,您可以轻松找出以后再回过头来发生的事情。