Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/google-app-engine/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何确保在继续执行其余代码之前保存app engine ndb根实体_Python_Google App Engine_Google Cloud Datastore - Fatal编程技术网

Python 如何确保在继续执行其余代码之前保存app engine ndb根实体

Python 如何确保在继续执行其余代码之前保存app engine ndb根实体,python,google-app-engine,google-cloud-datastore,Python,Google App Engine,Google Cloud Datastore,我当前正在尝试在用户首次登录时在我的app engine项目中创建CustomUser实体。我希望CustomUser实体是唯一的,并且我希望防止同一实体被多次创建。如果我可以在实体创建时为它提供一个祖先,这将是相当容易的,因为这将使事务非常一致 不幸的是,情况并非如此,因为CustomUser实体是根实体,因此它最终将是一致的,而不是强一致的。因此,有两次创建实体的情况,我希望避免这种情况,因为这将在以后导致问题 所以问题是,有没有一种方法可以防止实体被多次创建?或者至少使祖先实体的提交强烈一

我当前正在尝试在用户首次登录时在我的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
作为密钥。然后,您可以执行一个key
get
,而不是一个查询,这也是非常一致的

首先,请注意,祖先并不一定真的存在。如果需要强一致性查询,可以使用任意键作为祖先


第二种选择是使用
user\u id
作为密钥。然后,您可以执行一个键
get
,而不是一个查询,这同样是非常一致的。

看看get\u或\u insert。这是一种跨国的方式来放置一个独特的实体。谢谢,这似乎是一个漂亮的快捷方式。请看get_或_insert。这是一种放置唯一实体的跨国方式。谢谢,这似乎是一个很好的快捷键Hanks,我查阅了您的建议,最终实现的解决方案是为不存在的祖先创建任意键,并在创建实体时将此键设置为父项。通过这样做,我可以通过将这个键指定为祖先,使get、put和查询具有强一致性。现在,我还使用由
user\u id
构造的键获取实体。代码现在运行良好,没有创建重复的条目。谢谢,我查阅了您的建议,最终实现的解决方案包括为不存在的祖先创建任意键,并在创建实体时将该键设置为父项。通过这样做,我可以通过将这个键指定为祖先,使get、put和查询具有强一致性。现在,我还使用由
user\u id
构造的键获取实体。代码现在运行良好,没有创建重复的条目。