def testTransaction(self): class MyModel(model.Model): text = model.StringProperty() key = model.Key(MyModel, 'babaz') self.assertEqual(key.get(), None) def callback(): # Emulate get_or_insert() a = key.get() if a is None: a = MyModel(text='baz', key=key) a.put() return a b = model.transaction(callback) self.assertNotEqual(b, None) self.assertEqual(b.text, 'baz') self.assertEqual(key.get(), b) key = model.Key(MyModel, 'bababaz') self.assertEqual(key.get(), None) c = model.transaction(callback, retry=0, entity_group=key) self.assertNotEqual(c, None) self.assertEqual(c.text, 'baz') self.assertEqual(key.get(), c)
def create_multi(cls, values): """Creates multiple unique values at once. :param values: A sequence of values to be unique. See :meth:`create`. :returns: A tuple (bool, list_of_keys). If all values were created, bool is True and list_of_keys is empty. If one or more values weren't created, bool is False and the list contains all the values that already existed in datastore during the creation attempt. """ # Maybe do a preliminary check, before going for transactions? # entities = model.get_multi(keys) # existing = [entity.key.id() for entity in entities if entity] # if existing: # return False, existing # Create all records transactionally. 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, []
def try_transition(self, next_state, **kwargs): """Transition (if conditions are met) to the given next_state if next state arg is set; otherwise (no specific next_state indicated) to a next state whose transition conditions are met. make_transit should return True if transition made; False, if error while making transition. try_transition returns None if no transition tried (no conditions met). """ # Note: wrapping the entire transition, including its condition checking, # in a transaction. This is overkill under many circumstances (so we might # want to change this if there are contention issues), and also takes # advantage of the fact that currently everything we need to operate on # is in the same entity group, but makes the semantics of the # implementation more clear. def _tx(): res = None if next_state: # if state specified, try to transit to it if self._check_transit_conds(next_state, **kwargs): res = self._make_transit(next_state, **kwargs) else: for ns in self.next_states: # take the first transition whose conditions are met if self._check_transit_conds(ns, **kwargs): res = self._make_transit(ns, **kwargs) logging.info("finished make_transit with res %s", res) break return res resp = model.transaction(_tx) # run the transaction return resp
def create_multi(cls, values): """Creates multiple unique values at once. :param values: A sequence of values to be unique. See :meth:`create`. :returns: A tuple (bool, list_of_keys). If all values were created, bool is True and list_of_keys is empty. If one or more values weren't created, bool is False and the list contains all the values that already existed in datastore during the creation attempt. """ keys = [cls.get_key(value) for value in values] # Preliminary check, before going for more expensive transactions. entities = model.get_multi(keys) existing = [entity.key.id() for entity in entities if entity] if existing: return False, existing # Create all records transactionally. created = [] entities = [cls(key=key) for key in keys] for entity in entities: txn = lambda: entity.put() if not entity.key.get() else None key = model.transaction(txn) if key: created.append(key) if created != keys: # A poor man's "rollback": delete all recently created records. model.delete_multi(created) return False, [k.id() for k in keys if k not in created] return True, []
def register(cls, **user_values): """Registers a new user.""" if 'password_raw' in user_values: user_values['password'] = security.create_password_hash( user_values.pop('password_raw'), bit_strength=12) user_values['username'] = username = user_values['name'].lower() user = User(key=cls.get_key(username), **user_values) # Unique auth id and email. unique_auth_id = 'User.auth_id:%s' % user_values['auth_id'] unique_email = 'User.email:%s' % user_values['email'] uniques = [unique_auth_id, unique_email] success, existing = unique_model.Unique.create_multi(uniques) if success: txn = lambda: user.put() if not user.key.get() else None if model.transaction(txn): return True, user else: unique_model.Unique.delete_multi(uniques) return False, ['username'] else: properties = [] if unique_auth_id in uniques: properties.append('auth_id') if unique_email in uniques: properties.append('email') return False, properties
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, []
def create(cls, value): """Creates a new unique value. :param value: The value to be unique, as a string. The value should include the scope in which the value must be unique (ancestor, namespace, kind and/or property name). For example, for a unique property `email` from kind `User`, the value can be `User.email:[email protected]`. In this case `User.email` is the scope, and `[email protected]` is the value to be unique. :returns: True if the unique value was created, False otherwise. """ entity = cls(key=model.Key(cls, value)) txn = lambda: entity.put() if not entity.key.get() else None return model.transaction(txn) is not None
def get_current_game(cls, hangout_id): """Retrieves the current game, or creates one if none exists.""" def _tx(): dirty = False hangout = cls.get_by_id(hangout_id) if not hangout: hangout = cls(id=hangout_id) dirty = True if hangout.current_game: game = hangout.current_game.get() else: game = Game.new_game(hangout) game.put() hangout.current_game = game.key dirty = True if dirty: model.put_multi([hangout, game]) return game return model.transaction(_tx)
def get_or_create_participant(cls, game_key, plus_id): """ Either return the participant associated with the given plus_is, or create a new participant with that id, and deal them some cards. """ def _tx(): game = game_key.get() participant = cls.get_by_id(plus_id, parent=game_key) if not participant: participant = cls(id=plus_id, parent=game_key) participant.channel_id = str(participant.key) participant.channel_token = channel.create_channel( participant.channel_id) participant.playing = True # deal the hand for the participant. # TODO - deal with the case where the player did not get any cards, # indicated if hand is None. hand = game.deal_hand(participant) # if not hand: # react usefully if there were not enough cards for their hand. model.put_multi([participant, game]) return participant return model.transaction(_tx)
def create(cls, value): entity = cls(key=model.Key(cls, value)) txn = lambda: entity.put() if not entity.key.get() else None return model.transaction(txn) is not None
def create_user(cls, _unique_email=True, **user_values): """Creates a new user. :param _unique_email: True to require the email to be unique, False otherwise. :param user_values: Keyword arguments to create a new user entity. Required ones are: - name - username - auth_id - email Optional keywords: - password_raw (a plain password to be hashed) The properties values of `username` and `auth_id` must be unique. Optionally, `email` can also be required to be unique. :returns: A tuple (boolean, info). The boolean indicates if the user was created. If creation succeeds, ``info`` is the user entity; otherwise it is a list of duplicated unique properties that caused the creation to fail. """ assert user_values.get('password') is None, \ 'Use password_raw instead of password to create new users' if 'password_raw' in user_values: user_values['password'] = security.generate_password_hash( user_values.pop('password_raw'), length=12) user_values['username'] = user_values['username'].lower() user_values['auth_id'] = user_values['auth_id'].lower() user = User(key=cls.get_key(user_values['auth_id']), **user_values) # Unique auth id and email. unique_username = '******' % user_values['username'] uniques = [unique_username] if _unique_email: unique_email = 'User.email:%s' % user_values['email'] uniques.append(unique_email) else: unique_email = None if uniques: success, existing = unique_model.Unique.create_multi(uniques) if success: txn = lambda: user.put() if not user.key.get() else None if model.transaction(txn): return True, user else: unique_model.Unique.delete_multi(uniques) return False, ['auth_id'] else: properties = [] if unique_username in uniques: properties.append('username') if unique_email in uniques: properties.append('email') return False, properties
def create(cls, value): entity = cls(key=model.Key(cls, value)) txn = lambda: entity.put() if not entity.key.get() else None return model.transaction(txn) or None