Chapter 19: Twitter in Twenty Minutes

Similar documents
Chapter 18: Persistence

App Engine Web App Framework

webapp2 Documentation

App Engine Web App Framework

CS4HS Using Google App Engine. Michael Parker

Form Processing in PHP

Rapid Development with Django and App Engine. Guido van Rossum May 28, 2008

Google App Engine Data Store. Google is BIG. Advanced Stuff.

CS2021- Week 10 Models and Views. Model, View, Controller. Web Development Model, Views, Controller Templates Databases

BEGINNER PHP Table of Contents

Google & the Cloud. GData, Mashup Editor, AppEngine. Gregor Hohpe Software Engineer Google, Inc. All rights reserved,

How to Claim Your GIAC Digital Badge

CS193X: Web Programming Fundamentals

NETB 329 Lecture 13 Python CGI Programming

Interactive Web Application

Hons. B.Sc. Degree in Software Engineering/Development. Web and Cloud Development

Developing Online Databases and Serving Biological Research Data

Building Python web app on GAE

ABSOLUTE FORM PROCESSOR ADMINISTRATION OPTIONS

Draft 1, Ch. 1-3, Spring '09

Developing Ajax Applications using EWD and Python. Tutorial: Part 2

Google GCP-Solution Architects Exam

Access your page by hovering over your campus and then choosing Staff Directory. Click on your name to enter your page.

212Posters Instructions

Kentico CMS Web Parts

COM1004 Web and Internet Technology

Navigating Your CrowdRise Dashboard Team Member Guide

Building Production Quality Apps on App Engine. Ken Ashcraft 5/29/2008

Understanding the Dumper Program Google Application Engine University of Michigan Informatics

PYTHON CGI PROGRAMMING

Creating a Custom TinyWebDB service

Islamic University of Gaza Faculty of Engineering Department of Computer Engineering ECOM Advanced Internet Technology Lab.

Website/Blog Admin Using WordPress

Mail Merge for Gmail v2.0

Firespring Analytics

Google Groups. Using, joining, creating, and sharing. content with groups. What's Google Groups? About Google Groups and Google Contacts

Introduction to Databases and SQL

HTML Element A pair of tags and the content these include are known as an element

If you re a Facebook marketer, you re likely always looking for ways to

(Refer Slide Time: 01:40)

No matter where I am or whose computer I am using, as long as I have Internet access, I can mange my Google Docs account. Even from my cell phone!

Placester Quick Start Guide

c360 Web Connect Configuration Guide Microsoft Dynamics CRM 2011 compatible c360 Solutions, Inc. c360 Solutions

Outline. Introducing Form. Introducing Forms 2/21/2013 INTRODUCTION TO WEB DEVELOPMENT AND HTML

Blogging using Wordpress

Austin Community College Google Apps Groups Step-by-Step Guide

1 Form Basics CSC309

Hello everyone! Page 1. Your folder should look like this. To start with Run your XAMPP app and start your Apache and MySQL.

Using web-based

WordPress is free and open source, meaning it's developed by the people who use it.

Chapter 7: The Internet

Lecture 6: More Arrays & HTML Forms. CS 383 Web Development II Monday, February 12, 2018

Get feedback by using Voice of the Customer surveys Install the Voice of the Customer solution Plan a survey Design a basic survey Distribute a

SocialMiner Configuration

Q1. What is JavaScript?

CMS Editing SomersetYFC.co.uk

CS50 Quiz Review. November 13, 2017

Master Syndication Gateway V2. User's Manual. Copyright Bontrager Connection LLC

Web Focused Programming With PHP

By the end of this section of the practical, the students should be able to:

HTTP. EC512 Spring /15/2015 EC512 - Prof. Thomas Skinner 1

Client Side JavaScript and AJAX

How to Use Google. Sign in to your Chromebook. Let s get started: The sign-in screen.

HTML Forms. By Jaroslav Mohapl

SQL Deluxe 2.0 User Guide

PHP 5 if...else...elseif Statements

Creating and Sharing a Google Calendar

CMPT 165 INTRODUCTION TO THE INTERNET AND THE WORLD WIDE WEB

CGI Architecture Diagram. Web browser takes response from web server and displays either the received file or error message.

How To Change My Wordpress Database

Privacy and Security in Online Social Networks Department of Computer Science and Engineering Indian Institute of Technology, Madras

PIC 40A. Lecture 19: PHP Form handling, session variables and regular expressions. Copyright 2011 Jukka Virtanen UCLA 1 05/25/12

Zendesk Instructions for End-Users

web.py Tutorial Tom Kelliher, CS 317 This tutorial is the tutorial from the web.py web site, with a few revisions for our local environment.

Amazon Mechanical Turk. Requester UI Guide API Version

Getting Help...71 Getting help with ScreenSteps...72

CERTIFICATE IN WEB PROGRAMMING

Geocaching HTML & BBCode FUNdamentals by Scott Aleckson (SSO JOAT)

Lecture 9a: Sessions and Cookies

Questionnaire 4.0 User Manual 2006/4/14

Web-based Apps in.net

Admin Center. Getting Started Guide

FREETOASTHOST WEBSITE INSTRUCTIONS

FROM 4D WRITE TO 4D WRITE PRO INTRODUCTION. Presented by: Achim W. Peschke

Table of Contents

<form>. input elements. </form>

Privacy and Security in Online Social Networks Department of Computer Science and Engineering Indian Institute of Technology, Madras

Autoresponder Guide. David Sharpe

BrainCert Enterprise LMS. Learning Management System (LMS) documentation Administrator Guide Version 3.0

Connecture Platform Manager

External HTML E-form Guide

CHAPTER 2 MARKUP LANGUAGES: XHTML 1.0

By Ryan Stevenson. Guidebook #2 HTML

CS2021-Week 9 - Forms. HTML Forms. Python Web Development. h?ps:// cs253/unit-2html. Form for Submitting input:

Google Sites 101. Mrs. Wilson

Google Docs Website (Sign in or create an account):

WEBSITE INSTRUCTIONS

How to use the Molecular Workbench (MW) authoring environment to modify an existing activity.

Book IX. Developing Applications Rapidly

Get Started with Sites

Transcription:

Chapter 19: Twitter in Twenty Minutes In the last chapter, we learned how to create and query persistent data with App Engine and Google's Datastore. This chapter continues with that discussion by stepping through a simplified Twitter application. 1. App Specification Create Twitter 0.1. The application should allow any user to enter tweets and should display the tweets of all users. For this version, we'll ignore who enters the tweets and user accounts altogether. 2. Database Design Our database needs are simple: we need a database class for storing tweets. Each tweet should include the status message and the date and time it was entered. In the Customer example of last chapter, all of the fields were of type StringProperty. There are, however, a number of property (field) types defined in the Google's Datastore API. For a list, see http://code.google.com/appengine/docs/python/datastore/ typesandpropertyclasses.html. For our Tweet class, we need one StringProperty for the status message, and a DateTimeProperty for the date. Here's the class, which we'll define in a file named model.py: from google.appengine.ext import db class Tweet(db.Model): statusmessage = db.stringproperty(required=true) submissiontime = db.datetimeproperty(auto_now=true) Recall that defining such a db.model class just sets up the structure of our persisent data, performing a function analagous to the SQL create function which creates a database table. Later, we'll create actual records for the table in response to the user submitting an HTML form.

The 'required=true' parameter of the statusmessage StringProperty specifies that any tweet records created must have a non-empty value in this field. The 'auto_now=true' parameter of the DateTimeProperty tells the DataStore to automatically set the date on each tweet record stored. 3. The HTML Template We'll implement this version of Twitter with a single HTML template named index.html. The page needs a form for entering tweets, and a scripting code for loop that will unfold and show all of the previous tweets. Here it is: <!-- required: a template variable 'tweets' which is a list of objects --> <!-- each of which has statusmessage and submissiontime fields. --> <h2> What are you doing right now? </h2> <form action="/tweetsubmit" method="get"> <input type="text" name="status" value="status"> <input type="submit" value="submit"> </form> <br/><br/> <h2> Previous Tweets {% for tweet in tweets %} {{tweet.status}} <br/> {{tweet.submissiontime}} {% endfor %} The headers (h2s), breaks, and form are all standard HTML. The code within the double-curly brackets is scripting code using Django syntax, and specifies the dynamic part of this page. In this case, the dynamic part of the page is for displaying the previous tweets that have been entered. As set up, this page is expecting a list of Tweet records named tweets to be sent by the controllers that render this page. It also expects that each of the tweet records in the list will have fields for statusmessage and submissiontime. These requirements are specified at the top of file within an HTML comment (<!--... -->). 4. The Config File The config file for this application is as simple as possible: application: dwtwitter version: 1

runtime: python api_version: 1 handlers: - url: /.* script: twitter_controller.py The only 'custom' parts of it are the application name dwtwitter, which must match the id we give the application when we register at appspot.com, and the name of the controller file, twitter_controller.py, where all of our controller code will be placed. 5. The Controller Our controller code is in twitter_controller.py, as specified in the config file. We need two event-handlers for this application, one to load the main page originally, and one that responds to the user entering a new tweet. Our first task is to specify the mappings between URL requests and the event-handlers we'll define. The two requests are "/" and "/tweetsubmit", the first pertaining to when the main page is loaded, the latter pertaining to the action defined within our HTML form for entering a tweet. Here is the mapping table we'll define near the bottom of the controller code: application = webapp.wsgiapplication( [ ('/', MainHandler), ('/tweetsubmit',tweetsubmithandler) ], debug=true) With those mappings defined, we are ready to define the event-handlers for our application. First, the MainHandler which will be called when the user first visits the site. Remember, all the handlers need to provide the HTML template with a single template variable named tweets which is a list of Tweet objects. Here's the code: class MainHandler(webapp.RequestHandler): tweetlist=db.gqlquery("select * from Tweets") template_values={'tweets':tweetlist} # render the page using the template engine path = os.path.join(os.path.dirname( file ),'index.html')

self.response.out.write(template.render(path,template_values)) The MainHandler just performs a query to get all of the previously entered Tweets. This variable is then inserted into the template_values with a key of 'tweets'. It is this key that must match the template variable in the HTML template: {% for tweet in tweets %} {{tweet.status}} <br/> {{tweet.submissiontime}} {% endfor %} Note that the variable 'tweet' in the HTML template is a place holder representing the currrent item in the list as it is traversed. So just 'tweets' and not 'tweet' correctly appears in the controller's template_values. The second handler needed is the TweetSubmitHandler. This handler will be called when the user clicks on the submit button of the form within the HTML: <form action="/tweetsubmit" method="get"> <input type="text" name="status" value="status"> <input type="submit" value="submit"> </form> If the user entered 'just chilling' in the text box, the following URL would be sent to the server: nameofsite.com/tweetsubmit?status='just chilling' The request is /tweetsubmit?status?'just chilling' The first part, /tweetsubmit, just causes the TweetSubmitHandler to be called due to our specification in the mapping table. When that handler is invoked, it can get to the request parameter status using function self.request.get. Here's the code for the event-handler: class TweetSubmitHandler(webapp.RequestHandler): status=self.request.get("status") twitter = Twitter() twitter.statusmessage=status twitter.put()

self.redirect('/') First, the handler gets the parameter named 'status' from the form parameters. If the user entered 'just chilling', that will be the value of the variable 'status'. It then creates a Twitter object, an instance of the database class we created near the top of this chapter. It then sets the 'statusmessage' field of the Twitter object to the variable status ('just chilling'). Finally, it calls 'put' to actually create the record in the database, and redirects to '/', which leads to the MainHandler being called again. That handler sets up our template_values with the previously entered tweets, including the one just entered.

Chapter 20: Account Management This chapter introduces the App Engine method for managing user accounts and user generated data in your applications. We continue with the Twitter example, and describe how to restrict the site to only validated users, how to keep track of each user's account information, and how to show each user only her tweets. Introduction If you use tools like Google Docs or Sites, you've probably signed on using a form like the following: With App Engine, you can easily create apps that manage user accounts in the same manner as these Google tools, including requiring that your users login with Google accounts, and having them sign in using the form shown above. App Engine provides this functionality with: The ability to specify, in the app.yaml configuration file, the pages of the site that should be password protected. Library code that provides easy access to the Google account information of your site visitors. You can use this information as is or write your own account objects that "wrap" the Google user objects and store additional information about your users.

Requiring Login In the app.yaml file, you specify how an application is configured, including how each request should be processed. Along with specifying the controller file, you can also state that a user must be logged in to access a page: handlers: - url: / script: app_controller.py - url: /.* script: app_controller.py login: required The first specification states that requests for the home page of the site ('/') should be dispatched to the controller code in app_controller.py, and that no login is required to visit that page. The second specifies that all other pages should also be dispatched to the same app_controller.py file, but that the user will have to login before visiting those pages. When a request arrives for login:required pages, the App Engine server first checks if the user is signed in with their Google account. If so, the controller code is invoked as usual. If not, the user is sent to Google's sign-in dialog, the one shown at the top of this chapter. If the user successfully logs in, the controller code is then invoked. Besides login:required, you can also specify pages that can only be accessed by site administrators with login:admin. Site administrators are the maintainers of the site, the ones behind the scenes who perform such operations as removing spam or blocking abusive users. For the programs you are writing, you are the one and only site administrator. The rules are the same as with the login:required pages, but App Engine will also check to see if the user logging in is an administrator of your site. You can specify administrators for your site in the site's dashboard at appspot.com-- just choose Developers in the left panel of the dashboard and you can send email invites for collaborators. For more information about App Engine config files, see http://code.google.com/appengine/docs/python/config/appconfig.html

Accessing User Information in the Controller Once a user has logged in to the application you can access her information- - Google nickname and email-- using App Engine's users module. Specifically, you can call 'users.get_current_user' to access the currently signed in user. This function returns a user object, which has fields 'nickname' and 'email'. Here's an example of controller code for accessing the user's information and putting it in the template_values for display: class SomeController(webapp.RequestHandler): user = users.get_current_user() nickname = user.nickname() template_values={'nickname':nickname} path = os.path.join(os.path.dirname( file ),'somepage.html') self.response.out.write(template.render(path,template_values)) For further information about the users module, see http://code.google.com/ appengine/docs/python/users/userclass.html. Marking the Submitter in User-Generated Data Web 2.0 applications like Facebook and Twitter store data for the user, including profile information, bookmarks, notes, and other data commonly referred to as user-generated. For such data, the application must store a user id along with the information being stored. For instance, consider once again the Twitter application from the previous chapter, and specifically the Tweet database class: from google.appengine.ext import db class Tweet(db.Model): statusmessage = db.stringproperty(required=true) submissiontime = db.datetimeproperty(auto_now=true) As written, the class records the time of submission, but it doesn't record who submitted the status message (the tweet). Fortunately, App Engine provides a special class called a UserProperty for storing such information: from google.appengine.ext import db class Tweet(db.Model):

statusmessage = db.stringproperty(required=true) submissiontime = db.datetimeproperty(auto_now=true) submitter = db.userproperty() A UserProperty object can be set to the user that is returned from the library function 'users.get_current_user()'. App Engine doesn't actually store a copy of the user's information, just a key that can get us back to that information later. The addition to the TweetSubmit handler is quite simple: class TweetSubmitHandler(webapp.RequestHandler): user = users.get_current_user() status=self.request.get("status") twitter = Twitter() twitter.statusmessage=status twitter.submitter = user twitter.put() self.redirect('/') Now, each time a tweet is submitted, it is marked with the user who submitted it. This is of course essential for being able to list only a user's tweets, which we didn't do in the original Twitter sample (we showed the tweets from all users). Let's reconsider the MainHandler: class MainHandler(webapp.RequestHandler): tweetlist=db.gqlquery("select * from Tweets") template_values={'tweets':tweetlist} # render the page using the template engine path = os.path.join(os.path.dirname( file ),'index.html') self.response.out.write(template.render(path,template_values)) The query in this version of the handler selects all tweets, independent of who submitted them. We can modify this with by making use of the users.get_current_user() function and a Where clause in the query: class MainHandler(webapp.RequestHandler): user = users.get_current_user() tweetlist=db.gqlquery("select * from Tweets where submitter=:1",user)

template_values={'tweets':tweetlist} # render the page using the template engine path = os.path.join(os.path.dirname( file ),'index.html') self.response.out.write(template.render(path,template_values)) When the query is made, it will only return tweets which have a sumbitter field that matches the current user. Custom User Accounts The Google user account objects only provide fields for nickname and email, and don't allow you to gather custom profile information about users. To track custom account information, you can "wrap" a Google UserProperty within a database class:: class Account(db.Model): first=db.stringproperty() last=db.stringproperty() user = db.userproperty() This class tracks a first and last name for each account, and links to the Google User account with the field user, which is defined as type UserProperty and points at a built-in Google user object. Because the Account class encloses Google user data along with additional data, it is termed a wrapper class. To make use of the Account class, you'll need to perform some bookkeeping when the user first logs into the application with her Google Account. In the MainHandler event-handler, you can check if the current Google user account object is associated with one of the wrapper Account objects. If it not, you just create a new Account instance in your event-handler, set it's fields, and store it in the database.you can set the user property to the value of 'users.current_user()' and, since you don't yet know the user's first and last name, set those to the default value of the Google nickname: # code within an event-handler user = users.get_current_user() # does this user already have an account? account = db.gql('select * from Account where user = :1',user) if not account: # no account exists yet for this user account= Account() account.user=user account.first=user.nickname() # or some other default value like ' '

account.last=user.nickname() account.put() # whether a new or existing account, send the user to profile page self.redirect('/profile?'+user.key()) # send them to the profile page With the call to 'redirect' in the sample, you can also immediately send the user to a profile page that allows her to set her first and last name (we'll show this later). Recording the Account with the Tweet Because you are now representing users with your Account objects instead of Google users directly, you'll need to modify the Tweet class to reference an account instead of a user: class Tweet (db.model): status=db.stringproperty() date = db.dateproperty(auto=true) submitter = db.userproperty() submitter = db.referenceproperty(account) You'll also need to modify the TweetSubmit controller: class TweetSubmitHandler(webapp.RequestHandler): user = users.get_current_user() accountquery = db.gqlquery('select * from Account where user=:1',user) account = accountquery.get() status=self.request.get("status") twitter = Twitter() twitter.statusmessage=status twitter.submitter = account twitter.put() self.redirect('/') Note that the db.gqlquery returns a query object, and not the result of the query. We can send a query directly in the template_values to a web page template to be processed, but sometimes we need to know the result(s) while still in the controller. In the above sample, this is the case as we want to set the submitter field of the just submitted tweet to the account. So we call get on the query, which returns the first, and in this case only, result. With this change, each tweet will now point to the additional information in the user's Account object. In our example, this additional information

includes the first and last name. You could also include information such as a profile image and thus show the user's picture with each tweet listing. Showing User Information with Tweets The tweetlist which our controller sends to the HTML template has all the information about the tweets, including a pointer to the submitter information. To show that information, we need only modify the HTML template. Here are the modifications: <!-- required: a template variable 'tweets' which is a list of objects --> <!-- each of which has statusmessage and submissiontime fields. --> <h2> What are you doing right now? </h2> <form action="/tweetsubmit" method="get"> <input type="text" name="status" value="status"> <input type="submit" value="submit"> </form> <br/><br/> <h2> Previous Tweets </h2> {% for tweet in tweets %} {{tweet.status}} <br/> {{tweet.submissiontime}} by&nbsp{{tweet.account.lastname}} {% endfor %} &nbsp is HTML for a blank space. tweet.account gives us an account object, which has the field lastname. Showing All Tweets or User Tweets Suppose that you want to allow the user to see either just her tweets, or tweets from all users, and for the latter you want to show the name of the submitter along with the tweet. Suppose also that you want the web page to provide links that let the user choose either my tweets or all tweets. First, you'll need to modify the HTML template to add links for the two choices. Here's the revised code: <!-- required: a template variable 'tweets' which is a list of objects - -> <!-- each of which has statusmessage and submissiontime fields. -- > <h2> What are you doing right now? </h2>

<form action="/tweetsubmit" method="get"> <input type="text" name="status" value="status"> <input type="submit" value="submit"> </form> <br/><br/> <h2> Previous Tweets {% for tweet in tweets %} {{tweet.status}} <br/> {{tweet.submissiontime}} by&nbsp{{tweet.account.lastname}} {% endfor %}<br/> <a href='/mytweets'>my Tweets</a>&nbsp <a href='/alltweets'>all Tweets</a> Links are specified with anchor tags and here the 'href' attribute is set to a request back to your server, not a full URL. The requests for the two links is 'mytweets' and 'alltweets' respectively. Second, you'll need to add handler mappings for '/mytweets' and '/alltweets' in the mapping table: application = webapp.wsgiapplication( [ ('/', MainHandler), ('/tweetsubmit',tweetsubmithandler), ('/mytweets', MainHandler), ('/alltweets', AllTweetHandler) ], debug=true) '/mytweets' is mapped to the MainHandler, since you've already modified that to show just the user's tweets. The 'alltweets' request will be handled by a new handler, AllTweetHandler. Here's the AllTweetHandler. It is the original MainHandler before we began considering users, and it uses a query with no Where clause: class AllTweetsHandler(webapp.RequestHandler): user = users.get_current_user() tweetlist=db.gqlquery("select * from Tweets") template_values={'tweets':tweetlist} # render the page using the template engine

path = os.path.join(os.path.dirname( file ),'index.html') self.response.out.write(template.render(path,template_values)) Adding Login and Logout Links to the App Sites that require login will have at least one page, accessible to anyone, that let's users see what the site is about. Your application will need to lead users from this page to Gogle's registration/login page. The users module provides a "create_login_url" function to get the URL for a Google login page. It also provides a "create_logout_url" function to allow users to logout. You can call both from your controller with code like the following: class OpenPageHandler(webapp.RequestHandler): user=users.get_current_user() if user: self.redirect('/profile?user='+user.key()) else: login_url=users.create_login_url(self.request.uri) logout_url=users.create_logout_url(self.request.uri) template_values={'login_url':login_url, 'logout_url':logout_url} # render the page using the template engine path = os.path.join(os.path.dirname( file ),'openpage.html') self.response.out.write(template.render(path,template_values)) In this example, if the user is already logged in, get_current_user returns an entry and the user is redirected to another page-- the home page for returning users of the system. If get_current_user returns Null, the current visitor is not a logged in user. In this case, create_login_url is called to get the URL for the Google login page. When create_login_url is called, you provide a parameter which is the URL that should be returned to once the user does login. In the sample above, this return URL is set to self.request.uri, so control will be returned to this controller code (this time with a non-null user entry). After obtaining the login_url and logout_url, the code above sticks them into a template_value. OpenPage.html can then display these links along with its other introductory information. Here's a sample HTML file with the links: <html> <body> <p> The purpose of this site is...</p> <a href={{login_url}}>login</a><br/> <a href={{logout_url}}>login</a><br/>

</body> </html> Note that there is also a 'create_logout_url' which, when clicked, logs a user out of the application. Both the login and logout_urls are often placed in the header and/or footer of each page. Profile Page Let's now consider the development of a user profile page. Knowing how to create them is important as they are common to most web 2.0 apps. Their development also is instructive in terms of some fundamental HTML and database issues. The profile page should display the user's information and allow them to edit it: To begin, we'll assume that each user can only view and edit their own profile page. Later, we'll consider a profile page that can be viewed by others. Let's begin with mapping the requests we'll need to controller eventhandlers. We'll need a request->handler that loads the profile page initially, and another for handling things when the user clicks on the submit button to update the info. Here are the additions to the mapping table: application = webapp.wsgiapplication( [ ('/', MainHandler), ('/tweetsubmit',tweetsubmithandler), ('/mytweets', MainHandler), ('/alltweets', AllTweetHandler) ('/profile',profilehandler) ('/ProfileSubmit',profileSubmitHandler) ], debug=true) The ProfileHandler controller just needs to get the current user's account object and place it into the template values so the profile.html template can

access it: class ProfileHandler(webapp.RequestHandler): user=users.get_current_user() query = db.gqlquery('select * from Account where user=:1',user) account=query.get() template_values={'account':account} # render the page using the template engine path = os.path.join(os.path.dirname( file ),'profile.html') self.response.out.write(template.render(path,template_values)) Next, let's consider the HTML template profile.html. Because we want the user to be able to view the existing account information, we'll set the value attribute of each text box in our form to a field of the template value 'account': <!-- account required as a template value --!> <form action="/profilesubmit" method="get"> First Name: <input type="text" name="first" value={{account.first}}><br/> Last Name:<input type="text" name="last" value={{account.last}}><br/> <input type="submit" value="submit"> </form> The data put into the value attribute appears in the text box when the form is rendered. So the current values of account.first and account.last will appear, as "David" and "Wolber" appeared in the form rendering above. When the user submits, we send the action '/profilesubmit' to the controller. Because of our mappings, profilesubmithandler is called. The job of this controller is to get the user's entries for the account fields and modify the account object in the database: class ProfileSubmitHandler(webapp.RequestHandler): user=users.get_current_user() query = db.gqlquery('select * from Account where user=:1',user) account=query.get() firstname = self.request.get('first') lastname = self.request.get('last') # modify the account object account.first=firstname account.last=lastname

account.put() self.redirect ('/profile') We query for the account object using the current user information. We then call self.requst.get to get the data from the input boxes. This data may be the same as what's in the database if the user didn't modify it. But we assume that its changed, modify the fields of the account object with the data from the form, and call put() to save the changes persistently in the database. Note that put() is used here as a database "update". In our previous code, put() was used to "insert" a new record in the database. The key difference is that here, put() was called on an object that was the result of a query-- an existing database record. In the tweet samples, we use the object creation statement: tweet= Tweet() to get a new recorde and then eventually call put to "insert" it into the database. A Public Profile Page The template page profile.html above shows the current user her information. There is no way for user X to view the page of user Y. Of course sometimes this is desired: for instance, if X is reading everyone's tweets and sees one from Y she likes, she may want to follow a link to view more information about Y. For this reason, most sites have some type of public profile page. A public profile page is different: it doesn't have a form, as no editing can take place. It also must be parameterized with a user id so it knows which user to display. With App Engine, every database class is given a field key which uniquely identifies each record. Even though the Account class has three fields listed: class Account(db.Model): first=db.stringproperty() last=db.stringproperty() user = db.userproperty() the system takes care of providing it with another field, key, which is a unique identifier.

This key can be used to send the publicprofile page a parameter that uniquely identifies which user to display. For instance, when tweets are listed, we can have the key of the submitter specified as a parameter to the publicprofile page: {% for tweet in tweets %} {{tweet.status}} <br/> {{tweet.submissiontime}} by <a href='publicprofile?key={{tweet.submitter.key}}'> {{tweet.submitter.last}}</a> {% endfor %}<br/> The anchor tag will show the submitter's last name and generate a request something like: publicprofile?key=734r78927423 when the link is clicked, the 734r78927423 being the key of the account who submitted that particular tweet. The mapping table must be updated to handle the publicprofile request: application = webapp.wsgiapplication( [ ('/', MainHandler), ('/tweetsubmit',tweetsubmithandler), ('/mytweets', MainHandler), ('/alltweets', AllTweetHandler) ('/profile',profilehandler) ('/ProfileSubmit',profileSubmitHandler) ('/publicprofile',publicprofilehandler) ], debug=true) and the PublicProfileHandler will access the account object using the key: class Public ProfileHandler(webapp.RequestHandler): user=users.get_current_user() query = db.gqlquery('select * from Account where user=:1',user) key = self.request.get('key') account=account.get(key) template_values={'account':account}

# render the page using the template engine path = os.path.join(os.path.dirname( file ),'publicprofile.html') self.response.out.write(template.render(path,template_values)) Here, self.request.get obtains the parameter key from the request URL. Each database class has a method get which accepts a key as a parameter. So Account.get(key) returns an account object with the given key. Given that we don't want the account information edited, we use a different HTML template file, publicprofile.html, to display the account information: <!-- account required as a template value --!> First Name: {{account.first}}<br/> Last Name: {{account.last}} Uploading Images Images can be displayed on a web page with the <img> tag, e.g., <img src='/images/birds.jpg' /> That code is sufficient for rendering static images that always appear on your pages. But what about images that are uploaded by a user? Such uploading is the very purpose of photo-sharing sites like Flickr, and is also common to Web 2.0 applications like Facebook which include pictures of all users. To handle such uploading, your site needs a form for allowing the user to choose a web image or one from their hard disk, and the uploaded files must be stored persistently on the server. Fortunately, App Engine provides some help for performing these common tasks. Storing Images Files can be stored in an AppEngine Database using the BlobProperty, which represents a bunch of bits. For instance, the following Account class stores a profile picture: class Account(db.Model): first=db.stringproperty() last=db.stringproperty() user = db.userproperty() profile_pic = db.blobproperty()

HTML forms allow for inputs of type "file". Such an input type prompts the user to choose a file with a dialog. For such forms, the enctype should be set to "multipart/form-data" and the method to "post", as the file might be large and "get" has limits on the number of bytes that can appear in the request. The following example let's the user choose an image file for their profile (and enter a first and last name): <!-- account required as a template value --!> <form action="/profilesubmit" enctype="multipart/form-data" method="post"> First Name: <input type="text" name="first" value={{account.first}}><br/> Last Name:<input type="text" name="last" value={{account.last}}><br/ Profile Picture: <input type="file" name="picfile"><br/> <input type="submit" value="submit"> </form> The ProfileSubmitHandler must be modified to get the file name entered by the user and create the Blob of the files bits: class ProfileSubmitHandler(webapp.RequestHandler): user=users.get_current_user() query = db.gqlquery('select * from Account where user=:1',user) account=query.get() firstname = self.request.get('first') lastname = self.request.get('last') pic = self.request.get("picfile") # modify the account object account.first=firstname account.last=lastname if pic: account.profile_pic = db.blob(pic) account.put() self.redirect ('/profile') Displaying Stored Images Images stored as blobs aren't loaded as static files, so you can't use the config file to catch requests for them and keep a controller event-handler from being called. Instead, you'll need controller code to serve the pictures from the blobs stored in the database, and you'll need a special request for

uploading images. An event-hanlder like the following will suffice: class ImageHandler (webapp.requesthandler): key=self.request.get('key') account = db.get(key) if account.profile_pic: self.response.headers['content-type'] = "image/png" self.response.out.write(account.profile_pic) else: self.error(404) and you'll need to add a mapping: application = webapp.wsgiapplication(... ('/loadimage',imagehandler) ], debug=true) The ImageHandler controller code expects the key of an account as a parameter. It uses that key to get the account object from the database. Note that the image we are showing might or might not be that of the current user. Thus we can't just get the account object from the current user. Given the account object, ImageHandler then grabs the saved blob in account.profile_pic, and writes that file directly to the browser. Note that the content-type must be set. Given such a request->handler, an HTML template can display an image with code like the following: <img src='loadimage?key={{account.key}}' alt='no image'></img> The src of 'loadimage' results in ImageHandler being called. {{account.key}} will return the account's key field as the parameter to the controller.

Summary Web 2.0 is a read-write web with the users being key agents in the process. This chapter has described methods for handling the user accounts and profiles of an application, and techniques for storing and processing usergenerated data like tweets.