min read Author: klaas

Working with handlers (creating an API)

Basics and Conventions 

In PythonOnWheels a controller defines the external interface to your application, your: API through routes. The controller holds the programming logic of your app. It manipulates the model and responds with the right format. For web applications this will be often returning a view (an html representation). But a controller can also easily offer other API calls returning json, xml or other formats.

In PythonOnWheels every model can represent itself as json, xml and csv so you can really easy make REST APIs that support those formats. You can also add your own response formats as needed.

When working with handlers that are linked to models, the convention is that you give the handler the same name as the model.

When you want to give your todo model an API you create a REST handler and name it todo. PythonOnWheels will automatically link model and handler without any further configuration.

Working with shorties (very simple handlers)

Very often you link a handler to a model. The handler defines an API to create, alter or delete a model and returns the appropiate respones. But sometimes you need a handler that doesnt need a model at all. For this usecase you can use the shorties.

Shorties are standard handlers. You can find some pre-defined ones in handlers/shorties.py and handlers/hello_world.py. They perfectly define an API but they are pretty raw. Meaning not bound to conventions, linked to models etc. So they are in fact pure tornado handlers with the additional routing features of PythonOnWheels (like Flask routes, Class and Method routes)

This is how shorties look: (hello world example)

The shorty handler above adds the route yourhost:yourport/hello. When this URL is called with a browser or abny other client like curl. PythonOnWheels will receive the request, dispatch it to the HelloHandler and call the Method linked to th HTTP request type. In this case the method will only be called for GET requests. For any other request PythonOnWheels will respond with an appropiate error message. 

If you installed PythonOnWheels you can  just try it out directly because the HelloHandler is part of any generated app. So just generate an app, start it and call localhost:8080/hello.

If you don't know how to do that just follow the "build the super simple todo app tutorial".

Generating REST handlers

A very common use case is that you created a model, say a todo model and now you want to work with it. The typical actions you probably want to have are create, read, update and delete. This is so common that these action pack is called CRUD. It is also handy to list (returning all / or paging) and show models (return one specific model).

With PythonOnWheels you can generate handlers that automatically offer all those actions and also offers a REST API interface for it. API means that it automatically creates routes that map URLs and HTTP Verbs (GET, PUT...) to the right actions. Actions are just the corresponding methods of you handler.

So if you haven't done it, create a todo model now.

python generate_model.py -n todo -t sql

It is totally ok to create a NoSql model like -t tinydb or -t mongodb. Just remeber to give the handler the right type.

Now we create our REST handler

This is of course also done using a generator script. So all we do is executing:

python generate_handler.py -n todo -t sql --rest

Output:

CamelCased handler name:  Todo
---------------------------------------
 generating handler: Todo
----------------------------------------
... REST Handler
... created: /Users/khz/development/testapp/handlers/todo.py

This generates a fully working REST handler for us which is automatically linked to the to sql model and offers all the CRUD methods. This is how it looks like (or check in your app: handlers/todo.py)

You can see that it adds the class decorator @app.add_rest_routes and thats all. The mapping for all of the REST routes to the correspondin methods is done by PythonOnWheels. These are the default routes added.

You now already have an API.

Just start the server and send a request. (make sure you have at least upserted one model). If you don't know how to save a model read this.

To test your API just start the server:

python server.py

You will see infos about the routes and DB connections but the route info that intererst us the most is the

ROUTING: added RESTful routes for: Todo as /todo

If you see that it means that all the subroutes are created and you can now already call your API.

Let's test the API

We do not have any fancy HTML views so far (in fact it is just one line to generate the according views in PythonOnWheels but we will leave this out on purpose). So let's make a request using the requests library.

import requests
r = requests.get("http://localhost:8080/todo/list")

And let's inspect the result:

r.text
'{"status": 200, "message": "todo, index", "data": "{\\"id\\": 1, \\"votes\\": 0, \\"last_updated\\": \\"2019-01-04T22:32:38+00:00\\", \\"created_at\\": \\"2019-01-04T22:32:38+00:00\\", \\"title\\": \\"a first todo\\", \\"text\\": \\"\\"}", "next": null, "prev": null}'

We can also call the show API to show the todo with id==1

r = requests.get("http://localhost:8080/todo/show/1")

Inspect the response

r.text
'{"status": 200, "message": "todo show", "data": "{\\"id\\": 1, \\"votes\\": 0, \\"last_updated\\": \\"2019-01-04T22:32:38+00:00\\", \\"created_at\\": \\"2019-01-04T22:32:38+00:00\\", \\"title\\": \\"a first todo\\", \\"text\\": \\"\\"}", "next": null, "prev": null}'

Cool. You now have a working REST API.

Generating None REST handlers

Routing (Flask / RegEx)

Routing: RegEx and Flask like routes included .

You can set routes by simply adding a class decorator to the handler class or decorate methods directly.

Class decorators look like this:

@app.add_route("/test/([0-9]+)*", dispatch={"get" : "test"}) 

Method decorators look like this.

@route("/", dispatch=["get"])

The only difference is that you don't have to specify the method to call in a method decorator ;) PythonOnWheels will then call the method of your handler if the route and the HTTP method matches.

The first parameter of route decorators specifies the route. With PythonOnWheels you can use Flask routes or RegEx routes. Just what you prefer or your use case needs.

It's not witchcraft, just take a look:

Example for Flask routes:

@app.make_method_routes()
class HelloHandler(BaseHandler):
@route(r'/hello/<int:identifier>', dispatch=["get"])
 def hello(self, identifier=None):
self.write("Hello world! " + str(identifier))

This will call the hello method on a HTTP GET call on host:port/hello followed by an optional identifier.

For Regex routes use:

@app.make_method_routes()
class HelloHandler(BaseHandler):
@route(r'/test/([0-9]+)*', dispatch=["get"])
def hello(self, identifier=None):
self.write("Hello world! " + str(identifier))

To add a direct route: matching the regular expression : /test/([0-9+]) and then calling the given method of your handler class. The regex group ([0-9+]) will be handed as the first parameter to test(self, index)


Accessing Models and manipulating data

Responding views (html)

Responding json, xml, csv