Section 9.2. Digging Into the Project Controller Methods


9.2. Digging Into the Project Controller Methods

In the next section, we take a deeper look at how we can use MochiKit to make sending Ajax requests to the browser easier. Before we get there, let's take a look at the WhatWhat Status project controller and how it handles these Ajax requests, and what it sends back to the browser:

import turbogears           as tg import cherrypy             as http import commands import socket import md5 import time import random import os from turbogears             import identity, flash, validators from datetime               import datetime from whatwhat              import utils from textile                import textile from whatwhat.widgets      import widgets from whatwhat.model        import (Person, Project, Risk, Note, Question,                                     Answer, Issue, chance_codes, impact_codes,                                     status_codes, ProjectFile) class ProjectController(identity.SecureResource):     require = identity.not_anonymous()     @tg.expose()     def default(self, *args, **kwargs):         parts = http.request.path.rsplit('/')         if len(parts) == 3 and parts[-1].isdigit():             return self.project(parts[-1])     @tg.expose()     def index(self, *args, **kwargs):         project_id = self._active_project_id()         if project_id:             raise tg.redirect('/project/%s' % project_id)         return self.locate()     @tg.expose(template="whatwhat.templates.project.locate")     def locate(self):         all_projects = Project.select("parent_project_id is NULL order by upper(name)")         projects = [project for project in all_projects if not project.archived]         return dict(active_section='project',                     projects=projects)


Right at the top, notice again that we are requiring that every access to this controller or any of its methods will require that Identity knows who you are, and that you're properly logged in to the system.

The default, index, and expose methods in the preceding code provide ways for the project controller to find the right project in the database and ensure that the project method gets passed the correct values. We've already talked about how the default method works, but here's a quick reminder in case you skipped ahead: The default method is what you get if none of the other exposed methods are called directly. In other words, if your URL is http://localhost:8080/project/project, the project method is called; but if the URL doesn't match locate, project, toggle_closed_risks, or any of the other methods in this controller, the default method is called.

In this case, the default method grabs the URL string from CherryPy, splits it around the / character, and checks to see whether the last element in the parts list is a number. If it is, it assumes that number is the project you want to see and passes you on to the project's file with the correct ID.

The index method is what is called if you use a URL such as localhost:8080/project/.

WhatWhat Status uses cookies to remember which project you were looking at, and automatically brings you back to the right project page. To do this, the index method calls _active_project_id to check to see whether there is a cookie value set for the active project ID, and if so it sends the request along to the project controller with the value it got from the cookie. The code for the function that gets the cookie should be familiar by now, but here it is:

def _active_project_id(self):     if http.request.simpleCookie.has_key('active_project_id'):         return int(http.request.simpleCookie['active_project_id'].value)     return None


If _active_project_id can't retrieve an active project value from the cookie, it returns None, and the index method passes the user along to locate, which grabs a list of all the current projects and sends the user to whatwhat.templates.project.locate, which then displays a page with a drop-down list of projects. When the user selects a project from that page, the user gets sent to /projects/7 (or whatever project ID the user selected). Because there is no 7 method in the project controller, the default method gets called, and the user gets passed to the project with the correct number.

All of this might seem unnecessary, but it gives WhatWhat Status nice and pretty URLs for the project page, which are easy to use in our templates, and which also have the property of being RESTful. REST stands for REpresentational State Transfer, and it's a term used to describe a particular way of organizing resources on the web. We talk more about REST in Chapter 17, "CherryPy and TurboGears Decorators," but for now, it's probably enough to say that REST is an application design pattern that emphasizes URLs that are universal (every object in the system is represented by a unique URL), stateless (the URL is all that is needed to define that particular resource), and have a well-defined set of operations that can be performed on them.

In this case, FastTrack has a unique URL for every project in the system, and each element in the URL has a specific, well-defined meaning. /project/ indicates that we are looking at a project, and the /7 indicates that we are looking at the project with the ID of 7. If the WhatWhat Status authors implemented a delete mechanism for projects that answered to /project/7/delete, which deleted project 7, that would indicate a fully RESTFul interface.

The simpler alternative to defining these methods is to present WhatWhat Status users with links such as /project/project/7. Not only is this URL redundant, it could also be confusing to the user, and we'll constantly have to type that /project/ into all of our links, which is no fun either.

All of this brings us to the central method of the project controller project():

  @tg.expose(template="whatwhat.templates.project.project")   @tg.validate(validators=dict(project_id=validators.Int()))   def project(self, project_id):       project = Project.get(project_id)       user_person = Person.get(identity.current.user.id)       groupids = utils.getGroups()       http.response.simpleCookie['active_project_id'] = project_id       http.response.simpleCookie['active_project_id']['path'] = '/'       people = Person.select(orderBy='displayName')       return dict(active_section='project',                   project=project,                   chance_codes=chance_codes,                   impact_codes=impact_codes,                   status_codes=status_codes,                   show_closed_risks=bool(self._show_closed_risks()),                   show_closed_issues=bool(self._show_closed_issues()),                   show_all_notes=bool(self._show_all_notes()),                   people=people,                   user_person=user_person,                   groupids=groupids,                   risks_widget=widgets.risks_widget,                   issues_widget=widgets.issues_widget,                   questions_widget=widgets.questions_widget,                   notes_widget=widgets.notes_widget)


Even though this method is a bit longer than most of the ones we've seen so far, it is still conceptually simple. It gets the project object from the database, and assigns the project_id value to a cookie. Then it gets the current user's ID, and the groups they are a member of, and passes this data to the template in a dictionary. It also passes some codes from the model (which we've already seen), several Boolean values (which will determine whether closed Risks, Issues, and Notes will display), and a few widgets for good measure to the template.

This might seem like a lot of information to pass to one template; as you will see, however, the project template is totally dynamic and has the potential to display a lot of different information.

If you look at the project controller, you'll see a dozen or so additonal methods, most of which are going to be called by JavaScript functions on the project.kid template. So, rather than wade through these all at once, without understanding the context in which they are called, we'll circle back around and cover some of them as we look at the JavaScript that calls them.




Rapid Web Applications with TurboGears(c) Using Python to Create Ajax-Powered Sites
Rapid Web Applications with TurboGears: Using Python to Create Ajax-Powered Sites
ISBN: 0132433885
EAN: 2147483647
Year: 2006
Pages: 202

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net