Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move in ndb queries code snippets & tests #296

Merged
merged 2 commits into from
Apr 28, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Move in ndb queries code snippets & test.
  • Loading branch information
Jerjou Cheng committed Apr 28, 2016
commit 1cd17b07dd2ba58c54d13541182115b36e755aa7
11 changes: 11 additions & 0 deletions appengine/ndb/queries/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## App Engine Datastore NDB Queries Samples

This contains snippets used in the NDB queries documentation, demonstrating
various ways to make ndb queries.

<!-- auto-doc-link -->
These samples are used on the following documentation page:

> https://cloud.google.com/appengine/docs/python/ndb/queries

<!-- end-auto-doc-link -->
75 changes: 75 additions & 0 deletions appengine/ndb/queries/guestbook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import cgi

from google.appengine.datastore.datastore_query import Cursor
from google.appengine.ext import ndb
import webapp2


class Greeting(ndb.Model):
"""Models an individual Guestbook entry with content and date."""
content = ndb.StringProperty()
date = ndb.DateTimeProperty(auto_now_add=True)

@classmethod
def query_book(cls, ancestor_key):
return cls.query(ancestor=ancestor_key).order(-cls.date)


class MainPage(webapp2.RequestHandler):
GREETINGS_PER_PAGE = 20

def get(self):
guestbook_name = self.request.get('guestbook_name')
ancestor_key = ndb.Key('Book', guestbook_name or '*notitle*')
greetings = Greeting.query_book(ancestor_key).fetch(
self.GREETINGS_PER_PAGE)

self.response.out.write('<html><body>')

for greeting in greetings:
self.response.out.write(
'<blockquote>%s</blockquote>' % cgi.escape(greeting.content))

self.response.out.write('</body></html>')


class List(webapp2.RequestHandler):
GREETINGS_PER_PAGE = 10

def get(self):
"""Handles requests like /list?cursor=1234567."""
cursor = Cursor(urlsafe=self.request.get('cursor'))
greets, next_cursor, more = Greeting.query().fetch_page(
self.GREETINGS_PER_PAGE, start_cursor=cursor)

self.response.out.write('<html><body>')

for greeting in greets:
self.response.out.write(
'<blockquote>%s</blockquote>' % cgi.escape(greeting.content))

if more and next_cursor:
self.response.out.write('<a href="/list?cursor=%s">More...</a>' %
next_cursor.urlsafe())

self.response.out.write('</body></html>')


app = webapp2.WSGIApplication([
('/', MainPage),
('/list', List),
], debug=True)
66 changes: 66 additions & 0 deletions appengine/ndb/queries/guestbook_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import re

from google.appengine.ext import ndb
import guestbook
import pytest
import webtest


@pytest.fixture
def app(testbed):
return webtest.TestApp(guestbook.app)


def test_main(app):
# Add a greeting to find
guestbook.Greeting(
content='Hello world',
parent=ndb.Key('Book', 'brane3')).put()

# Add a greeting to not find.
guestbook.Greeting(
content='Flat sheet',
parent=ndb.Key('Book', 'brane2')).put()

response = app.get('/?guestbook_name=brane3')

assert response.status_int == 200
assert 'Hello world' in response.body
assert 'Flat sheet' not in response.body


def test_list(app):
# Add greetings to find
for i in range(11):
guestbook.Greeting(content='Greeting {}'.format(i)).put()

response = app.get('/list')
assert response.status_int == 200

assert 'Greeting 0' in response.body
assert 'Greeting 9' in response.body
assert 'Greeting 10' not in response.body

next_page = re.search(r'href="([^"]+)"', response.body).group(1)
assert next_page is not None

response = app.get(next_page)
assert response.status_int == 200

assert 'Greeting 0' not in response.body
assert 'Greeting 10' in response.body
assert 'More' not in response.body
234 changes: 234 additions & 0 deletions appengine/ndb/queries/snippets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from google.appengine.ext import ndb
from guestbook import Greeting
from snippets_models import * # noqa


def query_account_equality():
query = Account.query(Account.userid == 42)
return query


def query_account_inequality():
query = Account.query(Account.userid >= 40)
return query


def query_account_multiple_filters():
query = Account.query(Account.userid >= 40, Account.userid < 50)
return query


def query_account_in_steps():
query1 = Account.query() # Retrieve all Account entitites
query2 = query1.filter(Account.userid >= 40) # Filter on userid >= 40
query3 = query2.filter(Account.userid < 50) # Filter on userid < 50 too
return query1, query2, query3


def query_article_inequality():
query = Article.query(Article.tags != 'perl')
return query


def query_article_inequality_explicit():
query = Article.query(ndb.OR(Article.tags < 'perl',
Article.tags > 'perl'))
return query


def articles_with_tags_example():
# [START included_in_inequality]
Article(title='Perl + Python = Parrot',
stars=5,
tags=['python', 'perl'])
# [END included_in_inequality]
# [START excluded_from_inequality]
Article(title='Introduction to Perl',
stars=3,
tags=['perl'])
# [END excluded_from_inequality]


def query_article_in():
query = Article.query(Article.tags.IN(['python', 'ruby', 'php']))
return query


def query_article_in_equivalent():
query = Article.query(ndb.OR(Article.tags == 'python',
Article.tags == 'ruby',
Article.tags == 'php'))
return query


def query_article_nested():
query = Article.query(ndb.AND(Article.tags == 'python',
ndb.OR(Article.tags.IN(['ruby', 'jruby']),
ndb.AND(Article.tags == 'php',
Article.tags != 'perl'))))
return query


def query_greeting_order():
query = Greeting.query().order(Greeting.content, -Greeting.date)
return query


def query_greeting_multiple_orders():
query = Greeting.query().order(Greeting.content).order(-Greeting.date)
return query


def query_purchase_with_customer_key():
# [START purchase_with_customer_key_models]
class Customer(ndb.Model):
name = ndb.StringProperty()

class Purchase(ndb.Model):
customer = ndb.KeyProperty(kind=Customer)
price = ndb.IntegerProperty()
# [END purchase_with_customer_key_models]

def query_purchases_for_customer_via_key(customer_entity):
purchases = Purchase.query(
Purchase.customer == customer_entity.key).fetch()
return purchases

return Customer, Purchase, query_purchases_for_customer_via_key


def query_purchase_with_ancestor_key():
# [START purchase_with_ancestor_key_models]
class Customer(ndb.Model):
name = ndb.StringProperty()

class Purchase(ndb.Model):
price = ndb.IntegerProperty()
# [END purchase_with_ancestor_key_models]

def create_purchase_for_customer_with_ancestor(customer_entity):
purchase = Purchase(parent=customer_entity.key)
return purchase

def query_for_purchases_of_customer_with_ancestor(customer_entity):
purchases = Purchase.query(ancestor=customer_entity.key).fetch()
return purchases

return (Customer, Purchase,
create_purchase_for_customer_with_ancestor,
query_for_purchases_of_customer_with_ancestor)


def print_query():
print(Employee.query())
# -> Query(kind='Employee')
print(Employee.query(ancestor=ndb.Key(Manager, 1)))
# -> Query(kind='Employee', ancestor=Key('Manager', 1))


def query_contact_with_city():
query = Contact.query(Contact.addresses.city == 'Amsterdam')
return query


def query_contact_sub_entities_beware():
query = Contact.query(Contact.addresses.city == 'Amsterdam', # Beware!
Contact.addresses.street == 'Spear St')
return query


def query_contact_multiple_values_in_single_sub_entity():
query = Contact.query(Contact.addresses == Address(city='San Francisco',
street='Spear St'))
return query


def query_properties_named_by_string_on_expando():
property_to_query = 'location'
query = FlexEmployee.query(ndb.GenericProperty(property_to_query) == 'SF')
return query


def query_properties_named_by_string_for_defined_properties(keyword, value):
query = Article.query(Article._properties[keyword] == value)
return query


def query_properties_named_by_string_using_getattr(keyword, value):
query = Article.query(getattr(Article, keyword) == value)
return query


def order_query_results_by_property(keyword):
expando_query = FlexEmployee.query().order(ndb.GenericProperty('location'))

property_query = Article.query().order(Article._properties[keyword])

return expando_query, property_query


def print_query_keys(query):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

im prob missing something but it seems we don't include this in a region tag or use it..what's the point?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's included (like most of the stuff in this file) using an indented_block instead of a region_tag

for key in query.iter(keys_only=True):
print(key)


def reverse_queries():
# Set up.
q = Bar.query()
q_forward = q.order(Bar.key)
q_reverse = q.order(-Bar.key)

# Fetch a page going forward.
bars, cursor, more = q_forward.fetch_page(10)

# Fetch the same page going backward.
r_bars, r_cursor, r_more = q_reverse.fetch_page(10, start_cursor=cursor)

return (bars, cursor, more), (r_bars, r_cursor, r_more)


def fetch_message_accounts_inefficient(message_query):
message_account_pairs = []
for message in message_query:
key = ndb.Key('Account', message.userid)
account = key.get()
message_account_pairs.append((message, account))

return message_account_pairs


def fetch_message_accounts_efficient(message_query):
def callback(message):
key = ndb.Key('Account', message.userid)
account = key.get()
return message, account

message_account_pairs = message_query.map(callback)
# Now message_account_pairs is a list of (message, account) tuples.
return message_account_pairs


def fetch_good_articles_using_gql_with_explicit_bind():
query = ndb.gql("SELECT * FROM Article WHERE stars > :1")
query2 = query.bind(3)

return query, query2


def fetch_good_articles_using_gql_with_inlined_bind():
query = ndb.gql("SELECT * FROM Article WHERE stars > :1", 3)
return query
Loading