Python 如何确保在继续执行其余代码之前保存app engine ndb根实体
我当前正在尝试在用户首次登录时在我的app engine项目中创建CustomUser实体。我希望CustomUser实体是唯一的,并且我希望防止同一实体被多次创建。如果我可以在实体创建时为它提供一个祖先,这将是相当容易的,因为这将使事务非常一致 不幸的是,情况并非如此,因为CustomUser实体是根实体,因此它最终将是一致的,而不是强一致的。因此,有两次创建实体的情况,我希望避免这种情况,因为这将在以后导致问题 所以问题是,有没有一种方法可以防止实体被多次创建?或者至少使祖先实体的提交强烈一致以防止重复?这是我的代码和临时(黑客)解决方案Python 如何确保在继续执行其余代码之前保存app engine ndb根实体,python,google-app-engine,google-cloud-datastore,Python,Google App Engine,Google Cloud Datastore,我当前正在尝试在用户首次登录时在我的app engine项目中创建CustomUser实体。我希望CustomUser实体是唯一的,并且我希望防止同一实体被多次创建。如果我可以在实体创建时为它提供一个祖先,这将是相当容易的,因为这将使事务非常一致 不幸的是,情况并非如此,因为CustomUser实体是根实体,因此它最终将是一致的,而不是强一致的。因此,有两次创建实体的情况,我希望避免这种情况,因为这将在以后导致问题 所以问题是,有没有一种方法可以防止实体被多次创建?或者至少使祖先实体的提交强烈一
import time
import logging
from google.appengine.ext import ndb
# sample Model
class CustomUser(ndb.Model):
user_id = ndb.StringProperty(required=True)
some_data = ndb.StringProperty(required=True)
some_more_data = ndb.StringProperty(required=True)
externally_based_user_id = "id_taken_from_somewhere_else"
# check if this id already exists in the Model.
# If it does not exist yet, create it
user_entity = CustomUser.query(
CustomUser.user_id == externally_based_user_id,
ancestor=None).get()
if not user_entity:
# prepare the entity
user_entity = CustomUser(
user_id=externally_based_user_id,
some_data="some information",
some_more_data="even more information",
parent=None
)
# write the entity to ndb
user_key = user_entity.put()
# inform of success
logging.info("user " + str(user_key) + " created")
# eventual consistency workaround - loop and keep checking if the
# entity has already been created
#
# I understand that a while loop may not be the wisest solution.
# I can also use a for loop with n range to avoid going around the loop infinitely.
# Both however seem to be band aid solutions
while not entity_check:
entity_check = CustomUser.query(
CustomUser.user_id == externally_based_user_id,
ancestor=None).get()
# time.sleep to prevent the instance from consuming too much processing power and
# memory, although I'm not certain if this has any real effect apart from
# reducing the number of loops
if not entity_check:
time.sleep(0.5)
编辑:我最终使用的解决方案基于这两个方面。按照voscausa的建议,使用get_或_insert可以进一步简化。我一直坚持用通常的方式做事,让事情变得更清楚
import logging
from google.appengine.ext import ndb
# ancestor Model
# we can skip the creation of an empty class like this, and just use a string when
# retrieving a key
class PhantomAncestor(ndb.Model):
pass
# sample Model
class CustomUser(ndb.Model):
# user_id now considered redundance since we will be
# user_id = ndb.StringProperty(required=True)
some_data = ndb.StringProperty(required=True)
some_more_data = ndb.StringProperty(required=True)
externally_based_user_id = "id_taken_from_somewhere_else"
# construct the entity key using information we know.
# entity_key = ndb.Key(*arbitrary ancestor kind*, *arbitrary ancestor id*, *Model*, *user_id*)
# we can also use the string "PhantomAncestor" instead of passing in an empty class like so:
# entity_key = ndb.Key("SomeRandomString", externally_based_user_id, CustomUser, externally_based_user_id)
# check this page on how to construct a key: https://cloud.google.com/appengine/docs/python/ndb/keyclass#Constructors
entity_key = ndb.Key(PhantomAncestor, externally_based_user_id, CustomUser, externally_based_user_id)
# check if this id already exists in the Model.
user_entity = entity_key.get()
# If it does not exist yet, create it
if not user_entity:
# prepare the entity with the desired key value
user_entity = CustomUser(
# user_id=externally_based_user_id,
some_data="some information",
some_more_data="even more information",
parent=None,
# specify the custom key value here
id=externally_based_user_id
)
# write the entity to ndb
user_key = user_entity.put()
# inform of success
logging.info("user " + str(user_key) + " created")
# we should also be able to use CustomUser.get_and_insert to simplify the code further
这里有几件事 首先,请注意,祖先并不一定真的存在。如果需要强一致性查询,可以使用任意键作为祖先
第二种选择是使用
user\u id
作为密钥。然后,您可以执行一个keyget
,而不是一个查询,这也是非常一致的
首先,请注意,祖先并不一定真的存在。如果需要强一致性查询,可以使用任意键作为祖先
第二种选择是使用
user\u id
作为密钥。然后,您可以执行一个键get
,而不是一个查询,这同样是非常一致的。看看get\u或\u insert。这是一种跨国的方式来放置一个独特的实体。谢谢,这似乎是一个漂亮的快捷方式。请看get_或_insert。这是一种放置唯一实体的跨国方式。谢谢,这似乎是一个很好的快捷键Hanks,我查阅了您的建议,最终实现的解决方案是为不存在的祖先创建任意键,并在创建实体时将此键设置为父项。通过这样做,我可以通过将这个键指定为祖先,使get、put和查询具有强一致性。现在,我还使用由user\u id
构造的键获取实体。代码现在运行良好,没有创建重复的条目。谢谢,我查阅了您的建议,最终实现的解决方案包括为不存在的祖先创建任意键,并在创建实体时将该键设置为父项。通过这样做,我可以通过将这个键指定为祖先,使get、put和查询具有强一致性。现在,我还使用由user\u id
构造的键获取实体。代码现在运行良好,没有创建重复的条目。