Skip to content

Commit 67b8302

Browse files
committed
Merge pull request #255 from tseaver/170-query_namespaces
Fix #170: handle namespaces in queries
2 parents 628c430 + bfffdbf commit 67b8302

2 files changed

Lines changed: 38 additions & 14 deletions

File tree

gcloud/datastore/query.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ class Query(object):
4141
4242
:type dataset: :class:`gcloud.datastore.dataset.Dataset`
4343
:param dataset: The dataset to query.
44+
45+
:type namespace: string or None
46+
:param dataset: The namespace to which to restrict results.
4447
"""
4548

4649
OPERATORS = {
@@ -52,8 +55,9 @@ class Query(object):
5255
}
5356
"""Mapping of operator strings and their protobuf equivalents."""
5457

55-
def __init__(self, kind=None, dataset=None):
58+
def __init__(self, kind=None, dataset=None, namespace=None):
5659
self._dataset = dataset
60+
self._namespace = namespace
5761
self._pb = datastore_pb.Query()
5862
self._cursor = None
5963

@@ -66,11 +70,20 @@ def _clone(self):
6670
:rtype: :class:`gcloud.datastore.query.Query`
6771
:returns: a copy of 'self'.
6872
"""
69-
clone = self.__class__(dataset=self._dataset)
73+
clone = self.__class__(dataset=self._dataset,
74+
namespace=self._namespace)
7075
clone._pb.CopyFrom(self._pb)
7176
clone._cursor = self._cursor
7277
return clone
7378

79+
def namespace(self):
80+
"""This query's namespace
81+
82+
:rtype: string or None
83+
:returns: the namespace assigned to this query
84+
"""
85+
return self._namespace
86+
7487
def to_protobuf(self):
7588
"""Convert :class:`Query` instance to :class:`.datastore_v1_pb2.Query`.
7689
@@ -316,7 +329,10 @@ def fetch(self, limit=None):
316329
clone = self.limit(limit)
317330

318331
query_results = self.dataset().connection().run_query(
319-
query_pb=clone.to_protobuf(), dataset_id=self.dataset().id())
332+
query_pb=clone.to_protobuf(),
333+
dataset_id=self.dataset().id(),
334+
namespace=self._namespace,
335+
)
320336
entity_pbs, end_cursor = query_results[:2]
321337

322338
self._cursor = end_cursor

gcloud/datastore/test_query.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,44 @@ def _getTargetClass(self):
88

99
return Query
1010

11-
def _makeOne(self, kind=None, dataset=None):
12-
return self._getTargetClass()(kind, dataset)
11+
def _makeOne(self, kind=None, dataset=None, namespace=None):
12+
return self._getTargetClass()(kind, dataset, namespace)
1313

1414
def test_ctor_defaults(self):
15-
query = self._makeOne()
15+
query = self._getTargetClass()()
1616
self.assertEqual(query.dataset(), None)
1717
self.assertEqual(list(query.kind()), [])
1818
self.assertEqual(query.limit(), 0)
19+
self.assertEqual(query.namespace(), None)
1920

2021
def test_ctor_explicit(self):
2122
from gcloud.datastore.dataset import Dataset
2223

2324
_DATASET = 'DATASET'
2425
_KIND = 'KIND'
26+
_NAMESPACE = 'NAMESPACE'
2527
dataset = Dataset(_DATASET)
26-
query = self._makeOne(_KIND, dataset)
28+
query = self._makeOne(_KIND, dataset, _NAMESPACE)
2729
self.assertTrue(query.dataset() is dataset)
2830
kq_pb, = list(query.kind())
2931
self.assertEqual(kq_pb.name, _KIND)
32+
self.assertEqual(query.namespace(), _NAMESPACE)
3033

3134
def test__clone(self):
3235
from gcloud.datastore.dataset import Dataset
3336

3437
_DATASET = 'DATASET'
3538
_KIND = 'KIND'
3639
_CURSOR = 'DEADBEEF'
40+
_NAMESPACE = 'NAMESPACE'
3741
dataset = Dataset(_DATASET)
38-
query = self._makeOne(_KIND, dataset)
42+
query = self._makeOne(_KIND, dataset, _NAMESPACE)
3943
query._cursor = _CURSOR
4044
clone = query._clone()
4145
self.assertFalse(clone is query)
4246
self.assertTrue(isinstance(clone, self._getTargetClass()))
4347
self.assertTrue(clone.dataset() is dataset)
48+
self.assertEqual(clone.namespace(), _NAMESPACE)
4449
kq_pb, = list(clone.kind())
4550
self.assertEqual(kq_pb.name, _KIND)
4651
self.assertEqual(clone._cursor, _CURSOR)
@@ -101,7 +106,7 @@ def test_ancestor_w_non_key_non_list(self):
101106
query = self._makeOne()
102107
self.assertRaises(TypeError, query.ancestor, object())
103108

104-
def test_ancester_wo_existing_ancestor_query_w_key_and_propfilter(self):
109+
def test_ancestor_wo_existing_ancestor_query_w_key_and_propfilter(self):
105110
from gcloud.datastore.key import Key
106111
_KIND = 'KIND'
107112
_ID = 123
@@ -121,7 +126,7 @@ def test_ancester_wo_existing_ancestor_query_w_key_and_propfilter(self):
121126
self.assertEqual(p_pb.property.name, '__key__')
122127
self.assertEqual(p_pb.value.key_value, key.to_protobuf())
123128

124-
def test_ancester_wo_existing_ancestor_query_w_key(self):
129+
def test_ancestor_wo_existing_ancestor_query_w_key(self):
125130
from gcloud.datastore.key import Key
126131
_KIND = 'KIND'
127132
_ID = 123
@@ -137,7 +142,7 @@ def test_ancester_wo_existing_ancestor_query_w_key(self):
137142
self.assertEqual(p_pb.property.name, '__key__')
138143
self.assertEqual(p_pb.value.key_value, key.to_protobuf())
139144

140-
def test_ancester_wo_existing_ancestor_query_w_list(self):
145+
def test_ancestor_wo_existing_ancestor_query_w_list(self):
141146
from gcloud.datastore.key import Key
142147
_KIND = 'KIND'
143148
_ID = 123
@@ -153,7 +158,7 @@ def test_ancester_wo_existing_ancestor_query_w_list(self):
153158
self.assertEqual(p_pb.property.name, '__key__')
154159
self.assertEqual(p_pb.value.key_value, key.to_protobuf())
155160

156-
def test_ancester_clears_existing_ancestor_query_w_only(self):
161+
def test_ancestor_clears_existing_ancestor_query_w_only(self):
157162
_KIND = 'KIND'
158163
_ID = 123
159164
query = self._makeOne()
@@ -164,7 +169,7 @@ def test_ancester_clears_existing_ancestor_query_w_only(self):
164169
q_pb = after.to_protobuf()
165170
self.assertEqual(list(q_pb.filter.composite_filter.filter), [])
166171

167-
def test_ancester_clears_existing_ancestor_query_w_others(self):
172+
def test_ancestor_clears_existing_ancestor_query_w_others(self):
168173
_KIND = 'KIND'
169174
_ID = 123
170175
_NAME = 'NAME'
@@ -257,6 +262,7 @@ def test_fetch_default_limit(self):
257262
expected_called_with = {
258263
'dataset_id': _DATASET,
259264
'query_pb': query.to_protobuf(),
265+
'namespace': None,
260266
}
261267
self.assertEqual(connection._called_with, expected_called_with)
262268

@@ -266,6 +272,7 @@ def test_fetch_explicit_limit(self):
266272
_DATASET = 'DATASET'
267273
_KIND = 'KIND'
268274
_ID = 123
275+
_NAMESPACE = 'NAMESPACE'
269276
entity_pb = Entity()
270277
path_element = entity_pb.key.path_element.add()
271278
path_element.kind = _KIND
@@ -276,7 +283,7 @@ def test_fetch_explicit_limit(self):
276283
connection = _Connection(entity_pb)
277284
connection._cursor = _CURSOR
278285
dataset = _Dataset(_DATASET, connection)
279-
query = self._makeOne(_KIND, dataset)
286+
query = self._makeOne(_KIND, dataset, _NAMESPACE)
280287
limited = query.limit(13)
281288
entities = query.fetch(13)
282289
self.assertEqual(query._cursor, _CURSOR)
@@ -286,6 +293,7 @@ def test_fetch_explicit_limit(self):
286293
expected_called_with = {
287294
'dataset_id': _DATASET,
288295
'query_pb': limited.to_protobuf(),
296+
'namespace': _NAMESPACE,
289297
}
290298
self.assertEqual(connection._called_with, expected_called_with)
291299

0 commit comments

Comments
 (0)