Skip to content

Commit

Permalink
Merge pull request #548 from tseaver/514-add_datastore_put_api
Browse files Browse the repository at this point in the history
#514: add `datastore.put` API, remove `Entity.save'
  • Loading branch information
tseaver committed Jan 14, 2015
2 parents f980aad + d46ed28 commit 64aaed4
Show file tree
Hide file tree
Showing 12 changed files with 246 additions and 239 deletions.
2 changes: 1 addition & 1 deletion docs/_components/datastore-getting-started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Open a Python console and...
>>> entity = datastore.Entity(key=datastore.Key('Person'))
>>> entity['name'] = 'Your name'
>>> entity['age'] = 25
>>> entity.save()
>>> datastore.put([entity])
>>> list(Query(kind='Person').fetch())
[<Entity{...} {'name': 'Your name', 'age': 25}>]

Expand Down
2 changes: 1 addition & 1 deletion docs/_components/datastore-quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ you can create entities and save them::
>>> entity = datastore.Entity(key=datastore.Key('Person'))
>>> entity['name'] = 'Your name'
>>> entity['age'] = 25
>>> entity.save()
>>> datastore.put([entity])
>>> list(datastore.Query(kind='Person').fetch())
[<Entity{...} {'name': 'Your name', 'age': 25}>]

Expand Down
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Cloud Datastore
entity = datastore.Entity(key=datastore.Key('Person'))
entity['name'] = 'Your name'
entity['age'] = 25
entity.save()
datastore.put([entity])
Cloud Storage
~~~~~~~~~~~~~
Expand Down
1 change: 1 addition & 0 deletions gcloud/datastore/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
from gcloud.datastore.api import allocate_ids
from gcloud.datastore.api import delete
from gcloud.datastore.api import get
from gcloud.datastore.api import put
from gcloud.datastore.batch import Batch
from gcloud.datastore.connection import Connection
from gcloud.datastore.entity import Entity
Expand Down
29 changes: 29 additions & 0 deletions gcloud/datastore/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ def _get_dataset_id_from_keys(keys):
:returns: The dataset ID of the keys.
:raises: :class:`ValueError` if the key dataset IDs don't agree.
"""
if any(key is None for key in keys):
raise ValueError('None not allowed')

dataset_id = keys[0].dataset_id
# Rather than creating a list or set of all dataset IDs, we iterate
# and check. We could allow the backend to check this for us if IDs
Expand Down Expand Up @@ -133,6 +136,32 @@ def get(keys, missing=None, deferred=None, connection=None):
return entities


def put(entities, connection=None):
"""Save the entities in the Cloud Datastore.
:type entities: list of :class:`gcloud.datastore.entity.Entity`
:param entities: The entities to be saved to the datastore.
:type connection: :class:`gcloud.datastore.connection.Connection`
:param connection: Optional connection used to connect to datastore.
"""
if not entities:
return

connection = connection or _implicit_environ.CONNECTION

current = _BATCHES.top
in_batch = current is not None
if not in_batch:
keys = [entity.key for entity in entities]
dataset_id = _get_dataset_id_from_keys(keys)
current = Batch(dataset_id=dataset_id, connection=connection)
for entity in entities:
current.put(entity)
if not in_batch:
current.commit()


def delete(keys, connection=None):
"""Delete the keys in the Cloud Datastore.
Expand Down
20 changes: 10 additions & 10 deletions gcloud/datastore/demo/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
toy.update({'name': 'Toy'})

# Now let's save it to our datastore:
toy.save()
datastore.put([toy])

# If we look it up by its key, we should find it...
print(datastore.get([toy.key]))
Expand All @@ -55,7 +55,7 @@
entity = datastore.Entity(key)
entity['name'] = name
entity['age'] = age
entity.save()
datastore.put([entity])
# We'll start by look at all Thing entities:
query = datastore.Query(kind='Thing')

Expand All @@ -76,41 +76,41 @@

# You can also work inside a transaction.
# (Check the official docs for explanations of what's happening here.)
with datastore.Transaction():
with datastore.Transaction() as xact:
print('Creating and saving an entity...')
key = datastore.Key('Thing', 'foo')
thing = datastore.Entity(key)
thing['age'] = 10
thing.save()
xact.put(thing)

print('Creating and saving another entity...')
key2 = datastore.Key('Thing', 'bar')
thing2 = datastore.Entity(key2)
thing2['age'] = 15
thing2.save()
xact.put(thing2)

print('Committing the transaction...')

# Now that the transaction is commited, let's delete the entities.
datastore.delete([key, key2])

# To rollback a transaction, just call .rollback()
with datastore.Transaction() as t:
with datastore.Transaction() as xact:
key = datastore.Key('Thing', 'another')
thing = datastore.Entity(key)
thing.save()
t.rollback()
xact.put(thing)
xact.rollback()

# Let's check if the entity was actually created:
created = datastore.get([key])
print('yes' if created else 'no')

# Remember, a key won't be complete until the transaction is commited.
# That is, while inside the transaction block, thing.key will be incomplete.
with datastore.Transaction():
with datastore.Transaction() as xact:
key = datastore.Key('Thing') # partial
thing = datastore.Entity(key)
thing.save()
xact.put(thing)
print(thing.key) # This will still be partial

print(thing.key) # This will be complete
Expand Down
59 changes: 2 additions & 57 deletions gcloud/datastore/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@

"""Class for representing a single entity in the Cloud Datastore."""

from gcloud.datastore import _implicit_environ


class NoKey(RuntimeError):
"""Exception raised by Entity methods which require a key."""


class Entity(dict):
"""Entities are akin to rows in a relational database
Expand Down Expand Up @@ -70,8 +64,8 @@ class Entity(dict):
any decoding / encoding step.
:type key: :class:`gcloud.datastore.key.Key`
:param key: Optional key to be set on entity. Required for :meth:`save()`
or :meth:`reload()`.
:param key: Optional key to be set on entity. Required for
:func:`gcloud.datastore.put()`
:type exclude_from_indexes: tuple of string
:param exclude_from_indexes: Names of fields whose values are not to be
Expand Down Expand Up @@ -104,55 +98,6 @@ def exclude_from_indexes(self):
"""
return frozenset(self._exclude_from_indexes)

@property
def _must_key(self):
"""Return our key, or raise NoKey if not set.
:rtype: :class:`gcloud.datastore.key.Key`.
:returns: The entity's key.
:raises: :class:`NoKey` if no key is set.
"""
if self.key is None:
raise NoKey()
return self.key

def save(self, connection=None):
"""Save the entity in the Cloud Datastore.
.. note::
Any existing properties for the entity will be replaced by those
currently set on this instance. Already-stored properties which do
not correspond to keys set on this instance will be removed from
the datastore.
.. note::
Property values which are "text" (``unicode`` in Python2, ``str`` in
Python3) map to 'string_value' in the datastore; values which are
"bytes" (``str`` in Python2, ``bytes`` in Python3) map to
'blob_value'.
:type connection: :class:`gcloud.datastore.connection.Connection`
:param connection: Optional connection used to connect to datastore.
"""
connection = connection or _implicit_environ.CONNECTION

key = self._must_key
assigned, new_id = connection.save_entity(
dataset_id=key.dataset_id,
key_pb=key.to_protobuf(),
properties=dict(self),
exclude_from_indexes=self.exclude_from_indexes)

# If we are in a transaction and the current entity needs an
# automatically assigned ID, tell the transaction where to put that.
transaction = connection.transaction()
if transaction and key.is_partial:
transaction.add_auto_id_entity(self)

if assigned:
# Update the key (which may have been altered).
self.key = self.key.completed_key(new_id)

def __repr__(self):
if self.key:
return '<Entity%s %s>' % (self.key.path,
Expand Down
Loading

0 comments on commit 64aaed4

Please sign in to comment.