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 sample from appengine-guestbook-namespaces-python #106

Merged
merged 1 commit into from
Sep 23, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
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
32 changes: 32 additions & 0 deletions appengine/datastore_namespaces/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Guestbook with Namespaces

This application implements the Python [Guestbook sample][7] but uses
datastore namespaces to keep values in separate places.

Guestbook is an example application showing basic usage of Google App
Engine. Users can read & write text messages and optionaly log-in with
their Google account. Messages are stored in App Engine (NoSQL)
High Replication Datastore (HRD) and retrieved using a strongly consistent
(ancestor) query.

## Products
- [App Engine][1]

## Language
- [Python][2]

## APIs
- [NDB Datastore API][3]
- [Users API][4]

## Dependencies
- [webapp2][5]
- [jinja2][6]

[1]: https://developers.google.com/appengine
[2]: https://python.org
[3]: https://developers.google.com/appengine/docs/python/ndb/
[4]: https://developers.google.com/appengine/docs/python/users/
[5]: http://webapp-improved.appspot.com/
[6]: http://jinja.pocoo.org/docs/
[7]: https://github.com/GoogleCloudPlatform/appengine-guestbook-python/tree/part6-staticfiles
Empty file.
18 changes: 18 additions & 0 deletions appengine/datastore_namespaces/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
application: guestbook-namespaces
version: 1
runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /stylesheets
static_dir: stylesheets

- url: /.*
script: guestbook.application

libraries:
- name: webapp2
version: latest
- name: jinja2
version: latest
73 changes: 73 additions & 0 deletions appengine/datastore_namespaces/appengine_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright 2010 Google Inc. All Rights Reserved.

"""
Manages the namespace for the application.

This file presents ways an ISV (Independent Software Vendor) might use
namespaces to distribute the guestbook application to different corporate
clients. The original guestbook.py is left unchanged. Our namespace choosing
hook is run when datastore or memcache attempt to resolve the namespace.
When defined in appengine_config.py the lib_config mechanism substitutes this
function for the default definition which returns None. This hopefully shows
how easy it can be to make an existing app namespace aware.

Setting _NAMESPACE_PICKER has the following effects:

If _USE_SERVER_NAME, we read the server name
foo.guestbook-isv.appspot.com and set the namespace.

If _USE_GOOGLE_APPS_DOMAIN, we allow the namespace manager to infer the
namespace from the request.

If _USE_COOKIE, then the ISV might have a gateway page that sets a cookie
called 'namespace' for example, and we read this cookie and set the namespace
to its value. Note this is not a secure use of cookies.

Other possibilities not implemented here include using a mapping from user to
namespace and possibly setting a namespace cookie from this mapping. If the
mapping is stored in datastore, we would probably not wish to look it up on
every query.
"""

__author__ = '[email protected] (Nicholas Verne)'


import Cookie
import os

from google.appengine.api import namespace_manager


_USE_SERVER_NAME = 0
_USE_GOOGLE_APPS_DOMAIN = 1
_USE_COOKIE = 2

_NAMESPACE_PICKER = _USE_SERVER_NAME


def namespace_manager_default_namespace_for_request():
"""Determine which namespace is to be used for a request.

The value of _NAMESPACE_PICKER has the following effects:

If _USE_SERVER_NAME, we read server name
foo.guestbook-isv.appspot.com and set the namespace.

If _USE_GOOGLE_APPS_DOMAIN, we allow the namespace manager to infer
the namespace from the request.

If _USE_COOKIE, then the ISV might have a gateway page that sets a
cookie called 'namespace', and we set the namespace to the cookie's value
"""
name = None

if _NAMESPACE_PICKER == _USE_SERVER_NAME:
name = os.environ['SERVER_NAME']
elif _NAMESPACE_PICKER == _USE_GOOGLE_APPS_DOMAIN:
name = namespace_manager.google_apps_namespace()
elif _NAMESPACE_PICKER == _USE_COOKIE:
cookies = os.getenv('HTTP_COOKIE', None)
if cookies:
name = Cookie.BaseCookie(cookies).get('namespace')

return name
85 changes: 85 additions & 0 deletions appengine/datastore_namespaces/guestbook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import os
import urllib

from google.appengine.api import users
from google.appengine.ext import ndb

import jinja2

import webapp2

JINJA_ENVIRONMENT = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
extensions=['jinja2.ext.autoescape'])

DEFAULT_GUESTBOOK_NAME = 'default_guestbook'


# We set a parent key on the 'Greetings' to ensure that they are all in the
# same entity group. Queries across the single entity group will be consistent.
# However, the write rate should be limited to ~1/second.

def guestbook_key(guestbook_name=DEFAULT_GUESTBOOK_NAME):
"""Constructs a Datastore key for a Guestbook entity with guestbook_name"""
return ndb.Key('Guestbook', guestbook_name)


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


class MainPage(webapp2.RequestHandler):

def get(self):
guestbook_name = self.request.get('guestbook_name',
DEFAULT_GUESTBOOK_NAME)
greetings_query = Greeting.query(
ancestor=guestbook_key(guestbook_name)).order(-Greeting.date)
greetings = greetings_query.fetch(10)

if users.get_current_user():
url = users.create_logout_url(self.request.uri)
url_linktext = 'Logout'
else:
url = users.create_login_url(self.request.uri)
url_linktext = 'Login'

template_values = {
'greetings': greetings,
'guestbook_name': urllib.quote_plus(guestbook_name),
'url': url,
'url_linktext': url_linktext,
}

template = JINJA_ENVIRONMENT.get_template('index.html')
self.response.write(template.render(template_values))


class Guestbook(webapp2.RequestHandler):

def post(self):
# We set the same parent key on the 'Greeting' to ensure each Greeting
# is in the same entity group. Queries across the single entity group
# will be consistent. However, the write rate to a single entity group
# should be limited to ~1/second.
guestbook_name = self.request.get('guestbook_name',
DEFAULT_GUESTBOOK_NAME)
greeting = Greeting(parent=guestbook_key(guestbook_name))

if users.get_current_user():
greeting.author = users.get_current_user()

greeting.content = self.request.get('content')
greeting.put()

query_params = {'guestbook_name': guestbook_name}
self.redirect('/?' + urllib.urlencode(query_params))


application = webapp2.WSGIApplication([
('/', MainPage),
('/sign', Guestbook),
], debug=True)
35 changes: 35 additions & 0 deletions appengine/datastore_namespaces/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!DOCTYPE html>
{% autoescape true %}
<html>

<head>
<link type="text/css" rel="stylesheet" href="/stylesheets/main.css" />
</head>

<body>
{% for greeting in greetings %}
{% if greeting.author %}
<b>{{ greeting.author.nickname() }}</b> wrote:
{% else %}
An anonymous person wrote:
{% endif %}
<blockquote>{{ greeting.content }}</blockquote>
{% endfor %}

<form action="/sign?guestbook_name={{ guestbook_name }}" method="post">
<div><textarea name="content" rows="3" cols="60"></textarea></div>
<div><input type="submit" value="Sign Guestbook"></div>
</form>

<hr>

<form>Guestbook name:
<input value="{{ guestbook_name }}" name="guestbook_name">
<input type="submit" value="switch">
</form>

<a href="{{ url|safe }}">{{ url_linktext }}</a>

</body>
</html>
{% endautoescape %}
6 changes: 6 additions & 0 deletions appengine/datastore_namespaces/index.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
indexes:
- kind: Greeting
ancestor: yes
properties:
- name: date
direction: desc
4 changes: 4 additions & 0 deletions appengine/datastore_namespaces/stylesheets/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
body {
font-family: Verdana, Helvetica, sans-serif;
background-color: #DDDDDD;
}