Doing IT the App Engine Way
With the model defined, you can create the rest of your code. Create a file called myapp.py and pop the following code into it. Note that the code in this section is all contained in one file, but it's split up here so I can describe its function to you. Start with your imports:
import wsgiref.handlers from google.appengine.ext import webapp from google.appengine.ext import db from google.appengine.ext.webapp import template import myappDB
After importing the Python-standard WSGI reference implementation, three libraries are imported from App Engine: webapp provides a simple Web framework, db provides access to the App Engine datastore, and template provides access to App Engine's standard templating system (which is based on and built from Django's). Note that you've also imported the just-created myappDB module, which brings your model definitions into this program.
Every webapp needs to be told what to do when a user sends a default request from a browser to a server. Typically, this is requesting the index or home page. The first request handler provides that functionality:
class IndexHandler(webapp.RequestHandler):
def get(self):
html = template.render(
'templates/index.html', {}
)
self.response.out.write(html)
You've created a new class called IndexHandler that's inherited from webapp's RequestHandler. Within the class, a Python method called get is invoked whenever a request for the default page is processed. The method renders a template called index.html within the templates directory and assigns the rendered page to the html variable, which then is sent to standard output as the HTTP response, which eventually makes its way to the browser. The second parameter to the render() method is an empty hash (or “dictionary” to use the correct Python terminology). It's possible to send data (template variables) to the template engine, but you don't have to in this instance. The convention is to send an empty hash when there's no data for the template engine to process. I explain how to create templates later in this article. For now, note that any HTML used by your webapp is stored in the templates directory, which helps segregate view code from controller code.
The functionality required to leave a message has two parts. The first presents a small form that allows users to enter their e-mail addresses and messages. The second takes the submitted form data and stores it in the Google cloud. Here's a request handler class that implements both parts:
class LeaveCommentHandler(webapp.RequestHandler):
def get(self):
html = template.render(
'templates/comment.html', {}
)
self.response.out.write(html)
def post(self):
comment = myappDB.UserComment(
cust_email = self.request.get("c_email"),
cust_message = self.request.get("c_message")
)
comment.put()
self.redirect("/comments")
The get method in this class is essentially identical to the get method in the previous class, except in this method, you are rendering a different template called comment.html. The post method (which you didn't have in the last class) responds to a POST request sent from a browser. In other words, when users fill in the form rendered by the get method and then click the submit button, the data on the form is delivered to this request handler's post method. The code in your post method first creates a new instance of the model data by extracting two named form fields from the posted data (referred to as c_email and c_message in the HTML form). The submitted data is assigned to the data fields defined in the model and then stored in Google's cloud with a call to comment.put(). With that done, your code immediately redirects to a different URL (/comments), which causes another request handler's code to activate. That is, of course, assuming you have the “/comments” handler code written. And, here it is:
class DisplayCommentsHandler(webapp.RequestHandler):
def get(self):
comments_query = myappDB.UserComment.all()
comments = comments_query.fetch(1000)
html = template.render(
'templates/comments.html',
{'comments': comments}
)
self.response.out.write(html)
Your DisplayCommentsHandler code provides only GET functionality, as that's all that's required. Using App Engine's functional interface to the datastore, your get method first creates a query that asks for all the UserComment data, before fetching the first 1,000 comment-pairs from the query results. Your code then renders the comments.html template, passing in the (up to 1,000) comment-pairs to the templating engine. The rendered HTML returned from the template system then is sent to standard output as the HTTP response.
The limit of 1,000 is imposed by App Engine on all running webapps and is designed to limit the potential harm a “rogue” webapp could do to the App Engine infrastructure if left unchecked. By limiting the number of rows of data that can be fetched at once, App Engine can attempt to ensure that no one webapp hogs all of its resources. It's not really much of restriction. How many Web pages attempt to display more than a few hundred database records at a time? Obviously, if you have more than 1,000 rows in your datastore, you need to write some extra code to cycle through your data, 1,000 rows at a time, until you've exhausted it all.
Unlike the previous two request handlers, this latest one sends data to the templating system. The comment query results (a collection of e-mail addresses and messages) are passed to the template for further processing.
You may have noticed that there's no SQL used in the get method within the DisplayCommentsHandler request handler. Instead, you've used App Engine's API to request all the data from the datastore, from which you've then fetched 1,000 rows of data. Google's datastore technology, an integral component of App Engine, doesn't support SQL. It turns out that the datastore is not a relational database. Instead, it's based on Google's BigTable technology, which is a different beast altogether. Google does provide a query language of sorts in the guise of GQL, which looks a lot like SQL but doesn't do everything you are used to being able to do with SQL. For instance, there are no joins in GQL. Check out the App Engine datastore documentation for all the details to see if no SQL will be a deal-breaker for your app.
With the request handlers defined, all that's left to do is connect your application's URLs to the request handlers. Recall that the app.yaml file already has arranged for every URL request directed to your App Engine webapp to be handled by your myapp.py program. So, the question is, when the request gets to your program, what happens next? The answer is that your myapp.py program handles it, of course! When you imported webapp, you inherited functionality that allows you to link your URLs with your request handlers. Here's the code you need:
def main():
app = webapp.WSGIApplication(
[ ('/comment', LeaveCommentHandler),
('/comments', DisplayCommentsHandler),
('/.*', IndexHandler)],
debug = True
)
wsgiref.handlers.CGIHandler().run(app)
The WSGIApplication constructor takes a list of tuples detailing which URL invokes which request handler. Each of the URLs in the tuple list are regex patterns that are checked for a match to the URL that has been delivered to your program. If a match is found, the request handler is invoked. Note that in this code, you need a “catchall” regex to do something sensible when a delivered URL does not match one of the URL patterns. Here, you arrange to invoke the IndexHandler when no match is found. You've also switched debugging information on, which is useful during development, but it should be switched off when you deploy. With the URL routes ready, a call to the run method provided by wsgiref.handlers.CGIHandler starts your webapp.
The URL-routing and application-starting code is contained within the main function. This is deliberate, as App Engine looks for this function when initially loading and reloading your webapp. Its existence allows App Engine to optimize and cache your webapp. It also allows App Engine to avoid having to load your application every time a new request occurs, a criticism that continues to haunt CGI to this day. Of course, to test your webapp locally, you need to tell Python how to start it, which is accomplished with the classic Python idiom:
if __name__ == '__main__':
main()
And, that's it for your controller code. You've created code that does what you require when a URL request is sent to your webapp. All that's left is to create your view code, which, in this simple app, is a small set of HTML templates.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
Web Development News
Developer Poll
| Dynamic DNS—an Object Lesson in Problem Solving | May 21, 2013 |
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
| Non-Linux FOSS: Seashore | May 10, 2013 |
- RSS Feeds
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Using Salt Stack and Vagrant for Drupal Development
- New Products
- Validate an E-Mail Address with PHP, the Right Way
- Drupal Is a Framework: Why Everyone Needs to Understand This
- Download the Free Red Hat White Paper "Using an Open Source Framework to Catch the Bad Guy"
- A Topic for Discussion - Open Source Feature-Richness?
- Dynamic DNS—an Object Lesson in Problem Solving
- Tech Tip: Really Simple HTTP Server with Python
- Please correct the URL for Salt Stack's web site
2 hours 27 min ago - Android is Linux -- why no better inter-operation
4 hours 42 min ago - Connecting Android device to desktop Linux via USB
5 hours 10 min ago - Find new cell phone and tablet pc
6 hours 9 min ago - Epistle
7 hours 37 min ago - Automatically updating Guest Additions
8 hours 46 min ago - I like your topic on android
9 hours 32 min ago - This is the easiest tutorial
16 hours 8 min ago - Ahh, the Koolaid.
21 hours 47 min ago - git-annex assistant
1 day 3 hours ago








Comments
Great intro tutorial!
This has to be one of the two best intro-level tutorials on appengine. Thanks so much for writing it and putting the time into the explanations. It all works, makes sense, and your efforts are very much appreciated!