Skip to content

Commit 3a562d3

Browse files
author
Jerjou Cheng
committed
Move sample from appengine-guestbook-namespaces-python
1 parent 000ff37 commit 3a562d3

File tree

8 files changed

+253
-0
lines changed

8 files changed

+253
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Guestbook with Namespaces
2+
3+
This application implements the Python [Guestbook sample][7] but uses
4+
datastore namespaces to keep values in separate places.
5+
6+
Guestbook is an example application showing basic usage of Google App
7+
Engine. Users can read & write text messages and optionaly log-in with
8+
their Google account. Messages are stored in App Engine (NoSQL)
9+
High Replication Datastore (HRD) and retrieved using a strongly consistent
10+
(ancestor) query.
11+
12+
## Products
13+
- [App Engine][1]
14+
15+
## Language
16+
- [Python][2]
17+
18+
## APIs
19+
- [NDB Datastore API][3]
20+
- [Users API][4]
21+
22+
## Dependencies
23+
- [webapp2][5]
24+
- [jinja2][6]
25+
26+
[1]: https://developers.google.com/appengine
27+
[2]: https://python.org
28+
[3]: https://developers.google.com/appengine/docs/python/ndb/
29+
[4]: https://developers.google.com/appengine/docs/python/users/
30+
[5]: http://webapp-improved.appspot.com/
31+
[6]: http://jinja.pocoo.org/docs/
32+
[7]: https://github.com/GoogleCloudPlatform/appengine-guestbook-python/tree/part6-staticfiles

appengine/datastore_namespaces/__init__.py

Whitespace-only changes.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
application: guestbook-namespaces
2+
version: 1
3+
runtime: python27
4+
api_version: 1
5+
threadsafe: true
6+
7+
handlers:
8+
- url: /stylesheets
9+
static_dir: stylesheets
10+
11+
- url: /.*
12+
script: guestbook.application
13+
14+
libraries:
15+
- name: webapp2
16+
version: latest
17+
- name: jinja2
18+
version: latest
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Copyright 2010 Google Inc. All Rights Reserved.
2+
3+
"""
4+
Manages the namespace for the application.
5+
6+
This file presents ways an ISV (Independent Software Vendor) might use
7+
namespaces to distribute the guestbook application to different corporate
8+
clients. The original guestbook.py is left unchanged. Our namespace choosing
9+
hook is run when datastore or memcache attempt to resolve the namespace.
10+
When defined in appengine_config.py the lib_config mechanism substitutes this
11+
function for the default definition which returns None. This hopefully shows
12+
how easy it can be to make an existing app namespace aware.
13+
14+
Setting _NAMESPACE_PICKER has the following effects:
15+
16+
If _USE_SERVER_NAME, we read the server name
17+
foo.guestbook-isv.appspot.com and set the namespace.
18+
19+
If _USE_GOOGLE_APPS_DOMAIN, we allow the namespace manager to infer the
20+
namespace from the request.
21+
22+
If _USE_COOKIE, then the ISV might have a gateway page that sets a cookie
23+
called 'namespace' for example, and we read this cookie and set the namespace
24+
to its value. Note this is not a secure use of cookies.
25+
26+
Other possibilities not implemented here include using a mapping from user to
27+
namespace and possibly setting a namespace cookie from this mapping. If the
28+
mapping is stored in datastore, we would probably not wish to look it up on
29+
every query.
30+
"""
31+
32+
__author__ = 'nverne@google.com (Nicholas Verne)'
33+
34+
35+
import Cookie
36+
import os
37+
38+
from google.appengine.api import namespace_manager
39+
40+
41+
_USE_SERVER_NAME = 0
42+
_USE_GOOGLE_APPS_DOMAIN = 1
43+
_USE_COOKIE = 2
44+
45+
_NAMESPACE_PICKER = _USE_SERVER_NAME
46+
47+
48+
def namespace_manager_default_namespace_for_request():
49+
"""Determine which namespace is to be used for a request.
50+
51+
The value of _NAMESPACE_PICKER has the following effects:
52+
53+
If _USE_SERVER_NAME, we read server name
54+
foo.guestbook-isv.appspot.com and set the namespace.
55+
56+
If _USE_GOOGLE_APPS_DOMAIN, we allow the namespace manager to infer
57+
the namespace from the request.
58+
59+
If _USE_COOKIE, then the ISV might have a gateway page that sets a
60+
cookie called 'namespace', and we set the namespace to the cookie's value
61+
"""
62+
name = None
63+
64+
if _NAMESPACE_PICKER == _USE_SERVER_NAME:
65+
name = os.environ['SERVER_NAME']
66+
elif _NAMESPACE_PICKER == _USE_GOOGLE_APPS_DOMAIN:
67+
name = namespace_manager.google_apps_namespace()
68+
elif _NAMESPACE_PICKER == _USE_COOKIE:
69+
cookies = os.getenv('HTTP_COOKIE', None)
70+
if cookies:
71+
name = Cookie.BaseCookie(cookies).get('namespace')
72+
73+
return name
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import os
2+
import urllib
3+
4+
from google.appengine.api import users
5+
from google.appengine.ext import ndb
6+
7+
import jinja2
8+
9+
import webapp2
10+
11+
JINJA_ENVIRONMENT = jinja2.Environment(
12+
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
13+
extensions=['jinja2.ext.autoescape'])
14+
15+
DEFAULT_GUESTBOOK_NAME = 'default_guestbook'
16+
17+
18+
# We set a parent key on the 'Greetings' to ensure that they are all in the
19+
# same entity group. Queries across the single entity group will be consistent.
20+
# However, the write rate should be limited to ~1/second.
21+
22+
def guestbook_key(guestbook_name=DEFAULT_GUESTBOOK_NAME):
23+
"""Constructs a Datastore key for a Guestbook entity with guestbook_name"""
24+
return ndb.Key('Guestbook', guestbook_name)
25+
26+
27+
class Greeting(ndb.Model):
28+
"""Models an individual Guestbook entry with author, content, and date."""
29+
author = ndb.UserProperty()
30+
content = ndb.StringProperty(indexed=False)
31+
date = ndb.DateTimeProperty(auto_now_add=True)
32+
33+
34+
class MainPage(webapp2.RequestHandler):
35+
36+
def get(self):
37+
guestbook_name = self.request.get('guestbook_name',
38+
DEFAULT_GUESTBOOK_NAME)
39+
greetings_query = Greeting.query(
40+
ancestor=guestbook_key(guestbook_name)).order(-Greeting.date)
41+
greetings = greetings_query.fetch(10)
42+
43+
if users.get_current_user():
44+
url = users.create_logout_url(self.request.uri)
45+
url_linktext = 'Logout'
46+
else:
47+
url = users.create_login_url(self.request.uri)
48+
url_linktext = 'Login'
49+
50+
template_values = {
51+
'greetings': greetings,
52+
'guestbook_name': urllib.quote_plus(guestbook_name),
53+
'url': url,
54+
'url_linktext': url_linktext,
55+
}
56+
57+
template = JINJA_ENVIRONMENT.get_template('index.html')
58+
self.response.write(template.render(template_values))
59+
60+
61+
class Guestbook(webapp2.RequestHandler):
62+
63+
def post(self):
64+
# We set the same parent key on the 'Greeting' to ensure each Greeting
65+
# is in the same entity group. Queries across the single entity group
66+
# will be consistent. However, the write rate to a single entity group
67+
# should be limited to ~1/second.
68+
guestbook_name = self.request.get('guestbook_name',
69+
DEFAULT_GUESTBOOK_NAME)
70+
greeting = Greeting(parent=guestbook_key(guestbook_name))
71+
72+
if users.get_current_user():
73+
greeting.author = users.get_current_user()
74+
75+
greeting.content = self.request.get('content')
76+
greeting.put()
77+
78+
query_params = {'guestbook_name': guestbook_name}
79+
self.redirect('/?' + urllib.urlencode(query_params))
80+
81+
82+
application = webapp2.WSGIApplication([
83+
('/', MainPage),
84+
('/sign', Guestbook),
85+
], debug=True)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<!DOCTYPE html>
2+
{% autoescape true %}
3+
<html>
4+
5+
<head>
6+
<link type="text/css" rel="stylesheet" href="/stylesheets/main.css" />
7+
</head>
8+
9+
<body>
10+
{% for greeting in greetings %}
11+
{% if greeting.author %}
12+
<b>{{ greeting.author.nickname() }}</b> wrote:
13+
{% else %}
14+
An anonymous person wrote:
15+
{% endif %}
16+
<blockquote>{{ greeting.content }}</blockquote>
17+
{% endfor %}
18+
19+
<form action="/sign?guestbook_name={{ guestbook_name }}" method="post">
20+
<div><textarea name="content" rows="3" cols="60"></textarea></div>
21+
<div><input type="submit" value="Sign Guestbook"></div>
22+
</form>
23+
24+
<hr>
25+
26+
<form>Guestbook name:
27+
<input value="{{ guestbook_name }}" name="guestbook_name">
28+
<input type="submit" value="switch">
29+
</form>
30+
31+
<a href="{{ url|safe }}">{{ url_linktext }}</a>
32+
33+
</body>
34+
</html>
35+
{% endautoescape %}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
indexes:
2+
- kind: Greeting
3+
ancestor: yes
4+
properties:
5+
- name: date
6+
direction: desc
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
body {
2+
font-family: Verdana, Helvetica, sans-serif;
3+
background-color: #DDDDDD;
4+
}

0 commit comments

Comments
 (0)