Ich versuche gerade eine CustomUser-Entität in meinem App-Engine-Projekt zu erstellen, wenn sich ein Benutzer zum ersten Mal anmeldet. Ich möchte, dass CustomUser-Entitäten eindeutig sind, und ich möchte verhindern, dass dieselbe Entität mehr als einmal erstellt wird. Dies wäre ziemlich einfach zu tun, wenn ich es bei der Erstellung eines Entity mit einem Vorgänger versehen kann, da dies die Transaktion stark konsistent macht.So stellen Sie sicher, dass die Entität der App Engine ndb root gespeichert wird, bevor Sie mit dem restlichen Code fortfahren
Leider ist dies nicht der Fall, da eine CustomUser-Entität eine Root-Entität ist und daher letztendlich konsistent und nicht stark konsistent ist. Aus diesem Grund gibt es Fälle, in denen die Entität zweimal erstellt wird, was ich verhindern möchte, da dies später zu Problemen führen wird.
Die Frage ist also, gibt es eine Möglichkeit kann ich verhindern, dass die Entität mehr als einmal erstellt wird? Oder zumindest das Commit der Vorgängerinstanz stark konsistent machen, um Doppelungen zu vermeiden? Hier ist mein Code und eine Zwischenlösung (Hacky).
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)
EDIT: Lösung landete ich basiert auf den beiden Daniel Roseman's suggestions zu verbrauchen. Dies kann durch die Verwendung von get_or_insert, wie von voscausa vorgeschlagen, weiter vereinfacht werden. Ich habe mich daran gewöhnt, die üblichen Methoden zu nutzen, um die Dinge klarer zu machen.
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
Werfen Sie einen Blick auf get_or_insert. Es ist ein transnationaler Weg, eine einzigartige Einheit zu schaffen. – voscausa
Danke, das scheint eine nette Abkürzung zu sein –