Handling Requests With Controllers¶
Now that we can store data, we’re going to need a way to serve it to our users.
First, we’ll take a short detour to discuss one of the core parts of Ferris: Controller.
Ferris handles HTTP requests with Controllers. Controllers are classes that contain a collection of actions. Actions are methods that can be invoked via HTTP. Actions can setup context variables and pass those into views that can render template files or serve content such as JSON, ProtoRPC Message, and Blobstore Blobs.
Note
Controllers are usually UpperCamelCase plural nouns and are often tied to a particular model (i.e. Posts controller for the Post model, Daleks controller for the Dalek model). However, there are cases where you’ll make exceptions such as handlers that don’t have a model or those that have multiple models.
Saying Hello¶
Here’s a rather simple controller to get your feet wet. We’ll just say hello.
Create app/controllers/hello.py:
from ferris import Controller
class Hello(Controller):
def list(self):
return "Hello, is it me you're looking for?"
If you open http://localhost:8080/hello, you should see a friendly welcome.
Note
A dev server restart might be required at this point. Even though the App Engine server is pretty good about reloading code, it can sometimes miss when you add a new file. If examples do not work immediately for you, it’s recommended to try a server restart first.
You can also accept input from GET or POST, if you’d like.
Modify our list action:
def list(self):
return "Hello, %s" % self.request.params['name']
Now try to open http://localhost:8080/hello?name=Doctor
You may have noticed that Hello.list gets automatically routed to /hello. Ferris will automatically route the actions list, add, edit, view, and delete. These actions as well as other actions will be discussed shortly in the Routing and Scaffolding sections below.
Views & Templates¶
You’re not limited to returning simple strings from your controller. Ferris has a flexible view infrastructure. The default view provides the fantastic Jinja2 template engine.
Let’s create a template for our action at app/templates/hello/list.html:
{% extends 'layouts/default.html' %}
{% block layout_content %}
<h1>Hello, {{who}}</h1>
{% endblock %}
The syntax for Jinja2 can be a bit much to take in at once; I highly recommend checking out their excellent documentation. The gist of this is that we are going to use the default layout and add some content that says hello.
Now we just need to modify our list action:
def list(self):
self.context['who'] = self.request.params['name']
Notice here that we have to explicitly set the who variable for the template in the controller using self.context. This is our view context and everything that is stored in the dictionary self.context is made available inside of the template list.html.
If you load up http://localhost:8080/hello?name=Doctor, you should see a slightly prettier greeting.
Note
Ferris expects the template to be located at app/templates/controller/action.html. So the template for this action is at app/templates/hello/list.html. You can manually specify the template location by setting self.meta.view.template_name = '/path/template_name.html' in your controller’s action.
Routing¶
You’ve already seen how Hello.list gets automatically routed to /hello. Ferris will implicitly route the following actions to these urls:
Action | URL |
---|---|
list | /controller |
add | /controller/add |
view | /controller/<key> |
edit | /controller/<key>/edit |
delete | /controller/<key>/delete |
Notice the controller section of the url. The controller’s name is passed to inflector.underscore. For example, Posts becomes posts and FlyingMonsters becomes flying_monsters.
But what about actions that are not part of this list? You have to explicitly route them using the route decorator.
Modify our imports in the Hello handler:
from ferris import Controller, route
Add the following action to our Hello controller:
@route
def custom(self):
return "Something, indeed."
Go ahead and open http://localhost:8080/hello/custom.
You’ll notice that Ferris determines the url using the template /handler/action.
Ferris can also put parameters in the url as well. Let’s modify our custom function:
@route
def custom(self, text):
return "%s, indeed." % text
Opening http://localhost:8080/hello/custom gives us a 404. We must pass some text to the custom action.
Open http://localhost:8080/hello/custom/Yes. You should see “Yes, indeed.” The last argument in this url is passed as a parameter to the custom action.
You may also have multiple mapped arguments:
@route
def custom(self, text, person):
return "%s, %s, indeed." % (text, person)
Try with http://localhost:8080/hello/custom/Yes/sir
Note
You can set your own URLs for methods using the route_with decorator.
Next¶
Continue with Scaffolding