例如我有这个用户模型:
class User(ndb.Model):
email = ndb.StringProperty()
我希望仅在没有其他实体具有特定User
属性值的情况下,以原子方式添加新的email
实体。
在SQL中我可以做这样的事情来检查和插入事务:
begin;
if count(select * from User where email='user@email.com') > 0 {
insert into User values('user@email.com');
}
end;
但是在GAE中这是不允许的,因为在事务中不允许非祖先查询。我不想将电子邮件地址用作实体密钥,因为用户的稳定ID可能不是基于电子邮件地址。
答案 0 :(得分:2)
这个问题已在webapp2微框架中得到解决。请查看this文档以及this博客文章。带有基本样本的复制粘贴代码:
from google.appengine.ext.ndb import model
class Unique(model.Model):
@classmethod
def create_multi(cls, values):
keys = [model.Key(cls, value) for value in values]
entities = [cls(key=key) for key in keys]
func = lambda e: e.put() if not e.key.get() else None
created = [model.transaction(lambda: func(e)) for e in entities]
if created != keys:
# A poor man's "rollback": delete all recently created records.
model.delete_multi(k for k in created if k)
return False, [k.id() for k in keys if k not in created]
return True, []
@classmethod
def delete_multi(cls, values):
return model.delete_multi(model.Key(cls, v) for v in values)
# Assemble the unique values for a given class and attribute scope.
uniques = [
'User.username.%s' % username,
'User.auth_id.%s' % auth_id,
'User.email.%s' % email,
]
# Create the unique username, auth_id and email.
success, existing = Unique.create_multi(uniques)
if success:
# The unique values were created, so we can save the user.
user = User(username=username, auth_id=auth_id, email=email)
user.put()
return user
else:
# At least one of the values is not unique.
# Make a list of the property names that failed.
props = [name.split('.', 2)[1] for name in uniques]
raise ValueError('Properties %r are not unique.' % props)
答案 1 :(得分:1)
您可以在以下用例中遇到此问题:
解决此问题的一种方法是使用电子邮件地址作为实体名称(即密钥)。请注意,它不必是用户实体:它可以是自己的电子邮件实体 - 用户实体的子实体。它甚至可能根本没有其他属性,除非你需要它们。这样您就可以使用事务,因为电子邮件实体和用户实体属于同一个实体组。
答案 2 :(得分:0)
由于你没有祖先,你可能会在最后一次写作后碰到一些inconsistent state for some hundred milliseconds or so,但在现实生活中,当涉及到电子邮件时,这种情况非常罕见,但当然不可能发生
考虑到这一点,您可以通过以下方式检查电子邮件是否已存在:
if User.query(User.email == 'test@example.com').count() == 0:
# do something with the email, it is safe to assume that is unique :)
else:
# that email already taken