Skip to content

Commit 91065a3

Browse files
author
Jerjou Cheng
committed
Move in ndb queries code snippets & test.
1 parent 5ecf1bc commit 91065a3

File tree

6 files changed

+837
-0
lines changed

6 files changed

+837
-0
lines changed

appengine/ndb/queries/README.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
## App Engine Datastore NDB Queries Samples
2+
3+
This contains snippets used in the NDB queries documentation, demonstrating
4+
various ways to make ndb queries.
5+
6+
<!-- auto-doc-link -->
7+
These samples are used on the following documentation page:
8+
9+
> https://cloud.google.com/appengine/docs/python/ndb/queries
10+
11+
<!-- end-auto-doc-link -->

appengine/ndb/queries/guestbook.py

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Copyright 2016 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the 'License');
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an 'AS IS' BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import cgi
16+
17+
from google.appengine.datastore.datastore_query import Cursor
18+
from google.appengine.ext import ndb
19+
import webapp2
20+
21+
22+
class Greeting(ndb.Model):
23+
"""Models an individual Guestbook entry with content and date."""
24+
content = ndb.StringProperty()
25+
date = ndb.DateTimeProperty(auto_now_add=True)
26+
27+
@classmethod
28+
def query_book(cls, ancestor_key):
29+
return cls.query(ancestor=ancestor_key).order(-cls.date)
30+
31+
32+
class MainPage(webapp2.RequestHandler):
33+
GREETINGS_PER_PAGE = 20
34+
35+
def get(self):
36+
guestbook_name = self.request.get('guestbook_name')
37+
ancestor_key = ndb.Key('Book', guestbook_name or '*notitle*')
38+
greetings = Greeting.query_book(ancestor_key).fetch(
39+
self.GREETINGS_PER_PAGE)
40+
41+
self.response.out.write('<html><body>')
42+
43+
for greeting in greetings:
44+
self.response.out.write(
45+
'<blockquote>%s</blockquote>' % cgi.escape(greeting.content))
46+
47+
self.response.out.write('</body></html>')
48+
49+
50+
class List(webapp2.RequestHandler):
51+
GREETINGS_PER_PAGE = 10
52+
53+
def get(self):
54+
"""Handles requests like /list?cursor=1234567."""
55+
cursor = Cursor(urlsafe=self.request.get('cursor'))
56+
greets, next_cursor, more = Greeting.query().fetch_page(
57+
self.GREETINGS_PER_PAGE, start_cursor=cursor)
58+
59+
self.response.out.write('<html><body>')
60+
61+
for greeting in greets:
62+
self.response.out.write(
63+
'<blockquote>%s</blockquote>' % cgi.escape(greeting.content))
64+
65+
if more and next_cursor:
66+
self.response.out.write('<a href="/list?cursor=%s">More...</a>' %
67+
next_cursor.urlsafe())
68+
69+
self.response.out.write('</body></html>')
70+
71+
72+
app = webapp2.WSGIApplication([
73+
('/', MainPage),
74+
('/list', List),
75+
], debug=True)
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Copyright 2016 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import re
16+
17+
from google.appengine.ext import ndb
18+
import guestbook
19+
import pytest
20+
import webtest
21+
22+
23+
@pytest.fixture
24+
def app(testbed):
25+
return webtest.TestApp(guestbook.app)
26+
27+
28+
def test_main(app):
29+
# Add a greeting to find
30+
guestbook.Greeting(
31+
content='Hello world',
32+
parent=ndb.Key('Book', 'brane3')).put()
33+
34+
# Add a greeting to not find.
35+
guestbook.Greeting(
36+
content='Flat sheet',
37+
parent=ndb.Key('Book', 'brane2')).put()
38+
39+
response = app.get('/?guestbook_name=brane3')
40+
41+
assert response.status_int == 200
42+
assert 'Hello world' in response.body
43+
assert 'Flat sheet' not in response.body
44+
45+
46+
def test_list(app):
47+
# Add greetings to find
48+
for i in range(11):
49+
guestbook.Greeting(content='Greeting {}'.format(i)).put()
50+
51+
response = app.get('/list')
52+
assert response.status_int == 200
53+
54+
assert 'Greeting 0' in response.body
55+
assert 'Greeting 9' in response.body
56+
assert 'Greeting 10' not in response.body
57+
58+
next_page = re.search(r'href="([^"]+)"', response.body).group(1)
59+
assert next_page is not None
60+
61+
response = app.get(next_page)
62+
assert response.status_int == 200
63+
64+
assert 'Greeting 0' not in response.body
65+
assert 'Greeting 10' in response.body
66+
assert 'More' not in response.body

appengine/ndb/queries/snippets.py

+234
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# Copyright 2016 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from google.appengine.ext import ndb
16+
from guestbook import Greeting
17+
from snippets_models import * # noqa
18+
19+
20+
def query_account_equality():
21+
query = Account.query(Account.userid == 42)
22+
return query
23+
24+
25+
def query_account_inequality():
26+
query = Account.query(Account.userid >= 40)
27+
return query
28+
29+
30+
def query_account_multiple_filters():
31+
query = Account.query(Account.userid >= 40, Account.userid < 50)
32+
return query
33+
34+
35+
def query_account_in_steps():
36+
query1 = Account.query() # Retrieve all Account entitites
37+
query2 = query1.filter(Account.userid >= 40) # Filter on userid >= 40
38+
query3 = query2.filter(Account.userid < 50) # Filter on userid < 50 too
39+
return query1, query2, query3
40+
41+
42+
def query_article_inequality():
43+
query = Article.query(Article.tags != 'perl')
44+
return query
45+
46+
47+
def query_article_inequality_explicit():
48+
query = Article.query(ndb.OR(Article.tags < 'perl',
49+
Article.tags > 'perl'))
50+
return query
51+
52+
53+
def articles_with_tags_example():
54+
# [START included_in_inequality]
55+
Article(title='Perl + Python = Parrot',
56+
stars=5,
57+
tags=['python', 'perl'])
58+
# [END included_in_inequality]
59+
# [START excluded_from_inequality]
60+
Article(title='Introduction to Perl',
61+
stars=3,
62+
tags=['perl'])
63+
# [END excluded_from_inequality]
64+
65+
66+
def query_article_in():
67+
query = Article.query(Article.tags.IN(['python', 'ruby', 'php']))
68+
return query
69+
70+
71+
def query_article_in_equivalent():
72+
query = Article.query(ndb.OR(Article.tags == 'python',
73+
Article.tags == 'ruby',
74+
Article.tags == 'php'))
75+
return query
76+
77+
78+
def query_article_nested():
79+
query = Article.query(ndb.AND(Article.tags == 'python',
80+
ndb.OR(Article.tags.IN(['ruby', 'jruby']),
81+
ndb.AND(Article.tags == 'php',
82+
Article.tags != 'perl'))))
83+
return query
84+
85+
86+
def query_greeting_order():
87+
query = Greeting.query().order(Greeting.content, -Greeting.date)
88+
return query
89+
90+
91+
def query_greeting_multiple_orders():
92+
query = Greeting.query().order(Greeting.content).order(-Greeting.date)
93+
return query
94+
95+
96+
def query_purchase_with_customer_key():
97+
# [START purchase_with_customer_key_models]
98+
class Customer(ndb.Model):
99+
name = ndb.StringProperty()
100+
101+
class Purchase(ndb.Model):
102+
customer = ndb.KeyProperty(kind=Customer)
103+
price = ndb.IntegerProperty()
104+
# [END purchase_with_customer_key_models]
105+
106+
def query_purchases_for_customer_via_key(customer_entity):
107+
purchases = Purchase.query(
108+
Purchase.customer == customer_entity.key).fetch()
109+
return purchases
110+
111+
return Customer, Purchase, query_purchases_for_customer_via_key
112+
113+
114+
def query_purchase_with_ancestor_key():
115+
# [START purchase_with_ancestor_key_models]
116+
class Customer(ndb.Model):
117+
name = ndb.StringProperty()
118+
119+
class Purchase(ndb.Model):
120+
price = ndb.IntegerProperty()
121+
# [END purchase_with_ancestor_key_models]
122+
123+
def create_purchase_for_customer_with_ancestor(customer_entity):
124+
purchase = Purchase(parent=customer_entity.key)
125+
return purchase
126+
127+
def query_for_purchases_of_customer_with_ancestor(customer_entity):
128+
purchases = Purchase.query(ancestor=customer_entity.key).fetch()
129+
return purchases
130+
131+
return (Customer, Purchase,
132+
create_purchase_for_customer_with_ancestor,
133+
query_for_purchases_of_customer_with_ancestor)
134+
135+
136+
def print_query():
137+
print(Employee.query())
138+
# -> Query(kind='Employee')
139+
print(Employee.query(ancestor=ndb.Key(Manager, 1)))
140+
# -> Query(kind='Employee', ancestor=Key('Manager', 1))
141+
142+
143+
def query_contact_with_city():
144+
query = Contact.query(Contact.addresses.city == 'Amsterdam')
145+
return query
146+
147+
148+
def query_contact_sub_entities_beware():
149+
query = Contact.query(Contact.addresses.city == 'Amsterdam', # Beware!
150+
Contact.addresses.street == 'Spear St')
151+
return query
152+
153+
154+
def query_contact_multiple_values_in_single_sub_entity():
155+
query = Contact.query(Contact.addresses == Address(city='San Francisco',
156+
street='Spear St'))
157+
return query
158+
159+
160+
def query_properties_named_by_string_on_expando():
161+
property_to_query = 'location'
162+
query = FlexEmployee.query(ndb.GenericProperty(property_to_query) == 'SF')
163+
return query
164+
165+
166+
def query_properties_named_by_string_for_defined_properties(keyword, value):
167+
query = Article.query(Article._properties[keyword] == value)
168+
return query
169+
170+
171+
def query_properties_named_by_string_using_getattr(keyword, value):
172+
query = Article.query(getattr(Article, keyword) == value)
173+
return query
174+
175+
176+
def order_query_results_by_property(keyword):
177+
expando_query = FlexEmployee.query().order(ndb.GenericProperty('location'))
178+
179+
property_query = Article.query().order(Article._properties[keyword])
180+
181+
return expando_query, property_query
182+
183+
184+
def print_query_keys(query):
185+
for key in query.iter(keys_only=True):
186+
print(key)
187+
188+
189+
def reverse_queries():
190+
# Set up.
191+
q = Bar.query()
192+
q_forward = q.order(Bar.key)
193+
q_reverse = q.order(-Bar.key)
194+
195+
# Fetch a page going forward.
196+
bars, cursor, more = q_forward.fetch_page(10)
197+
198+
# Fetch the same page going backward.
199+
r_bars, r_cursor, r_more = q_reverse.fetch_page(10, start_cursor=cursor)
200+
201+
return (bars, cursor, more), (r_bars, r_cursor, r_more)
202+
203+
204+
def fetch_message_accounts_inefficient(message_query):
205+
message_account_pairs = []
206+
for message in message_query:
207+
key = ndb.Key('Account', message.userid)
208+
account = key.get()
209+
message_account_pairs.append((message, account))
210+
211+
return message_account_pairs
212+
213+
214+
def fetch_message_accounts_efficient(message_query):
215+
def callback(message):
216+
key = ndb.Key('Account', message.userid)
217+
account = key.get()
218+
return message, account
219+
220+
message_account_pairs = message_query.map(callback)
221+
# Now message_account_pairs is a list of (message, account) tuples.
222+
return message_account_pairs
223+
224+
225+
def fetch_good_articles_using_gql_with_explicit_bind():
226+
query = ndb.gql("SELECT * FROM Article WHERE stars > :1")
227+
query2 = query.bind(3)
228+
229+
return query, query2
230+
231+
232+
def fetch_good_articles_using_gql_with_inlined_bind():
233+
query = ndb.gql("SELECT * FROM Article WHERE stars > :1", 3)
234+
return query

0 commit comments

Comments
 (0)