Handling Users

Google's App Engine allows you to make use of Google accounts to help in user authorization. Often, you'll want to keep your own 'wrapper' accounts to keep additional information about your users. This lesson discusses how to do that.

 Consider a site that:
  • Allows users to register  by entering their first and last names and signing in with their Google account.
  • Adds them as 'Person' objects in the system. 'Person' is the name of the database table (class) that will wrap the Google user account.
  • Restricts them from registering as more than one 'Person' with the same Google account.
How does it work? First, in the app.yaml file, we specify that the root page (index.html) is not 'login required', but all other pages do require login (more information see App Engine config files):

handlers:
- url: /
  script: social_controller.py 
- url: /.*
  script: social_controller.py
  login: required

For the login:required pages, the system will take care of checking if the user is signed in. If he is not, it will send him to sign in using Google's sign-in dialogue. Google also takes care of sending the user back to the page that was being accessed after sign in.

We define our wrapper 'Person' class in the following way:

class Person(db.Model):
  first=db.StringProperty()
  last=db.StringProperty()
  user = db.UserProperty()

The important part is the 'user' property, which refers to a Google user account object. If the persons in our system have an associated user, we can always find the current user's additional information (in this case just first and last)

index.html is the only one where login is NOT required. When any visitor goes there, he can either click login to get to Google's login page, or enter a first and last name and click register:

** index.html **

<html>
<body>
    Already a member?
    <a href={{login_url}}   >Sign in</a>
    <h2>Not a member? Register here:</h2>
    <form action="/on_register" method="get">
      <div>first:<input type="text" name="first"></div>
      <div>last:<input type="text" name="last"></div>   
      <div><input type="submit" value="Register"></div>
    </form>
</body>
</html>

Here's the controller code that renders the above HTML template:

class MainPage(webapp.RequestHandler):
  def get(self):
    # this page is not login required, so a visitor could register
    user = users.get_current_user()
    if user:   # if we are indeed logged in
      self.redirect('/home')
    login_url=users.create_login_url(self.request.uri)  # if not logged in, we'll put up a link to login as well as registration
    template_values={'login_url':login_url}
    # 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))

Notice that we first to see if the user is already logged in. If they are, they're redirected to the home page.

If not, we setup the login_url template value and render the page. login_url uses the Google User class method 'create_login_url'. You just give it a URL which is where the login dialog should send the user once he logs in.


The index.html page also provides a form for registering. If the user enters a first and last name and submits, the /on_register action is called. Here's the corresponding controller class:

class RegisterHandler(webapp.RequestHandler):
  def get(self):
    # the yaml file requires login, so when we get here, we're logged in
    user = users.get_current_user()
    #if not user: 
      #self.redirect(users.create_login_url(self.request.uri))  # call google sign in page
    # see if this google user is already in our system
    pquery = db.GqlQuery("SELECT * FROM Person where user= :1",user)
    person = pquery.get()
    if person:  # dude has already registered
      message='you were already registered'
    else:
      person = Person()
      person.first=self.request.get('first')
      person.last=self.request.get('last')
      person.user=user
      person.put()
      message='welcome'
    logouturl = users.create_logout_url('/')
    template_values={'person':person,'message':message,'logouturl':logouturl}
    # render the page using the template engine
    path = os.path.join(os.path.dirname(__file__),'home.html')
    self.response.out.write(template.render(path,template_values))

on_register requires login because of how the app.yaml is setup. Thus, prior to this controller code being reached, the user will be asked to login to Google (or create an account). Then the code above is invoked.

This code creates a new Person, sets the first and last fields, and puts it to the database. It also sets up a logout_url template value so we can put a link on the resulting page that is rendered (home.html)

home.html just shows the user his first and last name as well as his Google nickname:
*** home.html***

<html>
<body>
    {% if message %}
      {{message}}
    {% endif %}
    <h2>Profile Info</h2>
    {{ person.first}}
    {{ person.last}}
    {{ person.user.nickname}}

<br/>
<a href={{logout_url}}>logout</a>
</body>
</html>

Here's documentation for the Google App Engine User API

Attachments (3)

  • app.yaml - on Apr 24, 2009 1:20 PM by David Wolber (version 1)
    1k Download
  • person.py - on Apr 24, 2009 1:20 PM by David Wolber (version 1)
    1k Download
  • social_controller.py - on Apr 24, 2009 2:10 PM by David Wolber (version 3 / earlier versions)
    4k Download

Recent site activity