Skip to content

Commit 5f28d27

Browse files
committed
Factor out mutation-setting logic from 'Connection'.
Toward #514.
1 parent 7c01fcc commit 5f28d27

File tree

2 files changed

+120
-56
lines changed

2 files changed

+120
-56
lines changed

gcloud/datastore/batch.py

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class Local(object):
2020
"""Placeholder for non-threaded applications."""
2121

2222
from gcloud.datastore import _implicit_environ
23+
from gcloud.datastore import helpers
2324
from gcloud.datastore import datastore_v1_pb2 as datastore_pb
2425

2526

@@ -209,15 +210,7 @@ def put(self, entity):
209210
if entity.key is None:
210211
raise ValueError("Entity must have a key")
211212

212-
key_pb = entity.key.to_protobuf()
213-
properties = dict(entity)
214-
exclude = tuple(entity.exclude_from_indexes)
215-
216-
self.connection.save_entity(
217-
self.dataset_id, key_pb, properties,
218-
exclude_from_indexes=exclude, mutation=self.mutation)
219-
220-
if entity.key.is_partial:
213+
if _assign_entity_to_mutation(self.mutation, entity):
221214
self._auto_id_entities.append(entity)
222215

223216
def delete(self, key):
@@ -232,8 +225,7 @@ def delete(self, key):
232225
raise ValueError("Key must be complete")
233226

234227
key_pb = key.to_protobuf()
235-
self.connection.delete_entities(
236-
self.dataset_id, [key_pb], mutation=self.mutation)
228+
helpers._add_keys_to_request(self.mutation.delete, [key_pb])
237229

238230
def begin(self):
239231
"""No-op
@@ -279,3 +271,35 @@ def __exit__(self, exc_type, exc_val, exc_tb):
279271
self.rollback()
280272
finally:
281273
_BATCHES.pop()
274+
275+
276+
def _assign_entity_to_mutation(mutation_pb, entity):
277+
"""Helper method for ``Batch.put``."""
278+
auto_id = entity.key.is_partial
279+
280+
key_pb = entity.key.to_protobuf()
281+
key_pb = helpers._prepare_key_for_request(key_pb)
282+
283+
if auto_id:
284+
insert = mutation_pb.insert_auto_id.add()
285+
else:
286+
insert = mutation_pb.upsert.add()
287+
288+
insert.key.CopyFrom(key_pb)
289+
290+
for name, value in entity.items():
291+
prop = insert.property.add()
292+
# Set the name of the property.
293+
prop.name = name
294+
295+
# Set the appropriate value.
296+
helpers._set_protobuf_value(prop.value, value)
297+
298+
if name in entity.exclude_from_indexes:
299+
if not isinstance(value, list):
300+
prop.value.indexed = False
301+
302+
for sub_value in prop.value.list_value:
303+
sub_value.indexed = False
304+
305+
return auto_id

gcloud/datastore/test_batch.py

Lines changed: 85 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def test_add_auto_id_entity_w_partial_key(self):
9999
batch = self._makeOne(dataset_id=_DATASET, connection=connection)
100100
entity = _Entity()
101101
key = entity.key = _Key(_Entity)
102-
key._partial = True
102+
key._id = None
103103

104104
batch.add_auto_id_entity(entity)
105105

@@ -128,35 +128,54 @@ def test_put_entity_w_partial_key(self):
128128
batch = self._makeOne(dataset_id=_DATASET, connection=connection)
129129
entity = _Entity(_PROPERTIES)
130130
key = entity.key = _Key(_DATASET)
131-
key._partial = True
131+
key._id = None
132132

133133
batch.put(entity)
134134

135-
self.assertEqual(
136-
connection._saved,
137-
[(_DATASET, key._key, _PROPERTIES, (), batch.mutation)])
135+
insert_auto_ids = list(batch.mutation.insert_auto_id)
136+
self.assertEqual(len(insert_auto_ids), 1)
137+
self.assertEqual(insert_auto_ids[0].key, key._key)
138+
upserts = list(batch.mutation.upsert)
139+
self.assertEqual(len(upserts), 0)
140+
deletes = list(batch.mutation.delete)
141+
self.assertEqual(len(deletes), 0)
138142
self.assertEqual(batch._auto_id_entities, [entity])
139143

140144
def test_put_entity_w_completed_key(self):
141145
_DATASET = 'DATASET'
142-
_PROPERTIES = {'foo': 'bar'}
146+
_PROPERTIES = {'foo': 'bar', 'baz': 'qux', 'spam': [1, 2, 3]}
143147
connection = _Connection()
144148
batch = self._makeOne(dataset_id=_DATASET, connection=connection)
145149
entity = _Entity(_PROPERTIES)
150+
entity.exclude_from_indexes = ('baz', 'spam')
146151
key = entity.key = _Key(_DATASET)
147152

148153
batch.put(entity)
149154

150-
self.assertEqual(
151-
connection._saved,
152-
[(_DATASET, key._key, _PROPERTIES, (), batch.mutation)])
155+
insert_auto_ids = list(batch.mutation.insert_auto_id)
156+
self.assertEqual(len(insert_auto_ids), 0)
157+
upserts = list(batch.mutation.upsert)
158+
self.assertEqual(len(upserts), 1)
159+
160+
upsert = upserts[0]
161+
self.assertEqual(upsert.key, key._key)
162+
props = dict([(prop.name, prop.value) for prop in upsert.property])
163+
self.assertTrue(props['foo'].indexed)
164+
self.assertFalse(props['baz'].indexed)
165+
self.assertTrue(props['spam'].indexed)
166+
self.assertFalse(props['spam'].list_value[0].indexed)
167+
self.assertFalse(props['spam'].list_value[1].indexed)
168+
self.assertFalse(props['spam'].list_value[2].indexed)
169+
170+
deletes = list(batch.mutation.delete)
171+
self.assertEqual(len(deletes), 0)
153172

154173
def test_delete_w_partial_key(self):
155174
_DATASET = 'DATASET'
156175
connection = _Connection()
157176
batch = self._makeOne(dataset_id=_DATASET, connection=connection)
158177
key = _Key(_DATASET)
159-
key._partial = True
178+
key._id = None
160179

161180
self.assertRaises(ValueError, batch.delete, key)
162181

@@ -168,9 +187,13 @@ def test_delete_w_completed_key(self):
168187

169188
batch.delete(key)
170189

171-
self.assertEqual(
172-
connection._deleted,
173-
[(_DATASET, [key._key], batch.mutation)])
190+
insert_auto_ids = list(batch.mutation.insert_auto_id)
191+
self.assertEqual(len(insert_auto_ids), 0)
192+
upserts = list(batch.mutation.upsert)
193+
self.assertEqual(len(upserts), 0)
194+
deletes = list(batch.mutation.delete)
195+
self.assertEqual(len(deletes), 1)
196+
self.assertEqual(deletes[0], key._key)
174197

175198
def test_commit(self):
176199
_DATASET = 'DATASET'
@@ -188,13 +211,13 @@ def test_commit_w_auto_id_entities(self):
188211
batch = self._makeOne(dataset_id=_DATASET, connection=connection)
189212
entity = _Entity({})
190213
key = entity.key = _Key(_DATASET)
191-
key._partial = True
214+
key._id = None
192215
batch._auto_id_entities.append(entity)
193216

194217
batch.commit()
195218

196219
self.assertEqual(connection._committed, [(_DATASET, batch.mutation)])
197-
self.assertFalse(key._partial)
220+
self.assertFalse(key.is_partial)
198221
self.assertEqual(key._id, _NEW_ID)
199222

200223
def test_as_context_mgr_wo_error(self):
@@ -214,9 +237,13 @@ def test_as_context_mgr_wo_error(self):
214237

215238
self.assertEqual(list(_BATCHES), [])
216239

217-
self.assertEqual(
218-
connection._saved,
219-
[(_DATASET, key._key, _PROPERTIES, (), batch.mutation)])
240+
insert_auto_ids = list(batch.mutation.insert_auto_id)
241+
self.assertEqual(len(insert_auto_ids), 0)
242+
upserts = list(batch.mutation.upsert)
243+
self.assertEqual(len(upserts), 1)
244+
self.assertEqual(upserts[0].key, key._key)
245+
deletes = list(batch.mutation.delete)
246+
self.assertEqual(len(deletes), 0)
220247
self.assertEqual(connection._committed, [(_DATASET, batch.mutation)])
221248

222249
def test_as_context_mgr_nested(self):
@@ -225,9 +252,9 @@ def test_as_context_mgr_nested(self):
225252
_PROPERTIES = {'foo': 'bar'}
226253
connection = _Connection()
227254
entity1 = _Entity(_PROPERTIES)
228-
key = entity1.key = _Key(_DATASET)
255+
key1 = entity1.key = _Key(_DATASET)
229256
entity2 = _Entity(_PROPERTIES)
230-
key = entity2.key = _Key(_DATASET)
257+
key2 = entity2.key = _Key(_DATASET)
231258

232259
self.assertEqual(list(_BATCHES), [])
233260

@@ -244,11 +271,22 @@ def test_as_context_mgr_nested(self):
244271

245272
self.assertEqual(list(_BATCHES), [])
246273

247-
self.assertEqual(
248-
connection._saved,
249-
[(_DATASET, key._key, _PROPERTIES, (), batch1.mutation),
250-
(_DATASET, key._key, _PROPERTIES, (), batch2.mutation)]
251-
)
274+
insert_auto_ids = list(batch1.mutation.insert_auto_id)
275+
self.assertEqual(len(insert_auto_ids), 0)
276+
upserts = list(batch1.mutation.upsert)
277+
self.assertEqual(len(upserts), 1)
278+
self.assertEqual(upserts[0].key, key1._key)
279+
deletes = list(batch1.mutation.delete)
280+
self.assertEqual(len(deletes), 0)
281+
282+
insert_auto_ids = list(batch2.mutation.insert_auto_id)
283+
self.assertEqual(len(insert_auto_ids), 0)
284+
upserts = list(batch2.mutation.upsert)
285+
self.assertEqual(len(upserts), 1)
286+
self.assertEqual(upserts[0].key, key2._key)
287+
deletes = list(batch2.mutation.delete)
288+
self.assertEqual(len(deletes), 0)
289+
252290
self.assertEqual(connection._committed,
253291
[(_DATASET, batch2.mutation),
254292
(_DATASET, batch1.mutation)])
@@ -274,9 +312,13 @@ def test_as_context_mgr_w_error(self):
274312

275313
self.assertEqual(list(_BATCHES), [])
276314

277-
self.assertEqual(
278-
connection._saved,
279-
[(_DATASET, key._key, _PROPERTIES, (), batch.mutation)])
315+
insert_auto_ids = list(batch.mutation.insert_auto_id)
316+
self.assertEqual(len(insert_auto_ids), 0)
317+
upserts = list(batch.mutation.upsert)
318+
self.assertEqual(len(upserts), 1)
319+
self.assertEqual(upserts[0].key, key._key)
320+
deletes = list(batch.mutation.delete)
321+
self.assertEqual(len(deletes), 0)
280322
self.assertEqual(connection._committed, [])
281323

282324

@@ -305,17 +347,6 @@ class _Connection(object):
305347
def __init__(self, *new_keys):
306348
self._commit_result = _CommitResult(*new_keys)
307349
self._committed = []
308-
self._saved = []
309-
self._deleted = []
310-
311-
def save_entity(self, dataset_id, key_pb, properties,
312-
exclude_from_indexes=(), mutation=None):
313-
self._saved.append((dataset_id, key_pb, properties,
314-
tuple(exclude_from_indexes), mutation))
315-
return self._save_result
316-
317-
def delete_entities(self, dataset_id, key_pbs, mutation=None):
318-
self._deleted.append((dataset_id, key_pbs, mutation))
319350

320351
def commit(self, dataset_id, mutation):
321352
self._committed.append((dataset_id, mutation))
@@ -329,23 +360,32 @@ class _Entity(dict):
329360

330361
class _Key(object):
331362
_MARKER = object()
363+
_kind = 'KIND'
332364
_key = 'KEY'
333-
_partial = False
334365
_path = None
335-
_id = None
366+
_id = 1234
336367
_stored = None
337368

338369
def __init__(self, dataset_id):
339370
self.dataset_id = dataset_id
340371

341372
@property
342373
def is_partial(self):
343-
return self._partial
374+
return self._id is None
344375

345376
def to_protobuf(self):
346-
return self._key
377+
from gcloud.datastore import datastore_v1_pb2
378+
key = self._key = datastore_v1_pb2.Key()
379+
# Don't assign it, because it will just get ripped out
380+
# key.partition_id.dataset_id = self.dataset_id
381+
382+
element = key.path_element.add()
383+
element.kind = self._kind
384+
if self._id is not None:
385+
element.id = self._id
386+
387+
return key
347388

348389
def completed_key(self, new_id):
349-
assert self._partial
390+
assert self.is_partial
350391
self._id = new_id
351-
self._partial = False

0 commit comments

Comments
 (0)