appengine/standard/ndb/transactions/main.py (114 lines of code) (raw):
# Copyright 2015 Google Inc.
#
# 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
import random
import urllib
import flask
# [START gae_ndb_transactions_import]
from google.appengine.api import taskqueue
from google.appengine.ext import ndb
# [END gae_ndb_transactions_import]
class Note(ndb.Model):
"""Models an individual Note entry with content."""
content = ndb.StringProperty()
def parent_key(page_name):
return ndb.Key("Parent", page_name)
app = flask.Flask(__name__)
@app.route("/")
def main_page():
page_name = flask.request.args.get("page_name", "default")
response = """
<html><body>
<h2>Permanent note page: %s</h2>""" % cgi.escape(
page_name
)
parent = parent_key(page_name)
notes = Note.query(ancestor=parent).fetch(20)
for note in notes:
response += "<h3>%s</h3>" % cgi.escape(note.key.id())
response += "<blockquote>%s</blockquote>" % cgi.escape(note.content)
response += """<hr>
<form action="/add?%s" method="post">
Submit Note: <input value="Title" name="note_title"><br>
<textarea value="Note" name="note_text" rows="4" cols="60">
</textarea>
<input type="submit" value="Etch in stone"></form>""" % urllib.urlencode(
{"page_name": page_name}
)
response += """
<hr>
<form>Switch page: <input value="%s" name="page_name">
<input type="submit" value="Switch"></form>
</body>
</html>""" % cgi.escape(
page_name, quote=True
)
return response
# [START gae_ndb_transactions_insert_standard]
@ndb.transactional
def insert_if_absent(note_key, note):
fetch = note_key.get()
if fetch is None:
note.put()
return True
return False
# [END gae_ndb_transactions_insert_standard]
# [START gae_ndb_transactions_insert_two_tries]
@ndb.transactional(retries=1)
def insert_if_absent_2_retries(note_key, note):
# do insert
# [END gae_ndb_transactions_insert_two_tries]
fetch = note_key.get()
if fetch is None:
note.put()
return True
return False
# [START gae_ndb_transactions_insert_cross_group]
@ndb.transactional(xg=True)
def insert_if_absent_xg(note_key, note):
# do insert
# [END gae_ndb_transactions_insert_cross_group]
fetch = note_key.get()
if fetch is None:
note.put()
return True
return False
# [START gae_ndb_transactions_insert_sometimes]
def insert_if_absent_sometimes(note_key, note):
# do insert
# [END gae_ndb_transactions_insert_sometimes]
fetch = note_key.get()
if fetch is None:
note.put()
return True
return False
# [START gae_ndb_transactions_insert_independent]
@ndb.transactional(propagation=ndb.TransactionOptions.INDEPENDENT)
def insert_if_absent_indep(note_key, note):
# do insert
# [END gae_ndb_transactions_insert_independent]
fetch = note_key.get()
if fetch is None:
note.put()
return True
return False
# [START gae_ndb_transactions_insert_task_queue]
@ndb.transactional
def insert_if_absent_taskq(note_key, note):
taskqueue.add(url=flask.url_for("taskq_worker"), transactional=True)
# do insert
# [END gae_ndb_transactions_insert_task_queue]
fetch = note_key.get()
if fetch is None:
note.put()
return True
return False
@app.route("/worker")
def taskq_worker():
pass
def pick_random_insert(note_key, note):
choice = random.randint(0, 5)
if choice == 0:
# [START gae_ndb_transactions_insert_standard_calling_2]
inserted = insert_if_absent(note_key, note)
# [END gae_ndb_transactions_insert_standard_calling_2]
elif choice == 1:
inserted = insert_if_absent_2_retries(note_key, note)
elif choice == 2:
inserted = insert_if_absent_xg(note_key, note)
elif choice == 3:
# [START gae_ndb_transactions_insert_sometimes_callback]
inserted = ndb.transaction(lambda: insert_if_absent_sometimes(note_key, note))
# [END gae_ndb_transactions_insert_sometimes_callback]
elif choice == 4:
inserted = insert_if_absent_indep(note_key, note)
elif choice == 5:
inserted = insert_if_absent_taskq(note_key, note)
return inserted
@app.route("/add", methods=["POST"])
def add_note():
page_name = flask.request.args.get("page_name", "default")
note_title = flask.request.form["note_title"]
note_text = flask.request.form["note_text"]
parent = parent_key(page_name)
choice = random.randint(0, 1)
if choice == 0:
# Use transactional function
# [START gae_ndb_transactions_insert_standard_calling_1]
note_key = ndb.Key(Note, note_title, parent=parent)
note = Note(key=note_key, content=note_text)
# [END gae_ndb_transactions_insert_standard_calling_1]
if pick_random_insert(note_key, note) is False:
return 'Already there<br><a href="%s">Return</a>' % flask.url_for(
"main_page", page_name=page_name
)
return flask.redirect(flask.url_for("main_page", page_name=page_name))
elif choice == 1:
# Use get_or_insert, which is transactional
note = Note.get_or_insert(note_title, parent=parent, content=note_text)
if note.content != note_text:
return 'Already there<br><a href="%s">Return</a>' % flask.url_for(
"main_page", page_name=page_name
)
return flask.redirect(flask.url_for("main_page", page_name=page_name))