Getting Started

Milla aims to be lightweight and easy to use. As such, it provides only the tools you need to build your application the way you want, without imposing any restrictions on how to do it.

Milla’s Components

Milla provides a small set of components that help you build your web application in a simple, efficient manner:

  • WSGI Application wrapper
  • Two types of URL Dispatchers:
    • Traversal (like CherryPy or Pyramid)
    • Routing (like Django or Pylons)
  • Authorization framework
  • Utility functions

Milla does not provide an HTTP server, so you’ll have to use one of the many implementations already available, such as Meinheld or Paste, or another application that understands WSGI, like Apache HTTPD with the mod_wsgi module.

Application Objects

The core class in a Milla-based project is its Application object. Application objects are used to set up the environment for the application and handle incoming requests. Application instances are WSGI callables, meaning they implement the standard application(environ, start_response) signature.

To set up an Application, you will need a URL dispatcher, which is an object that maps request paths to controller callables.

Choosing a URL Dispatcher

Milla provides two types of URL dispatchers by default, but you can create your own if neither of these suit your needs. The default dispatchers are modeled after the URL dispatchers of other popular web frameworks, but may have small differences.

A Milla application can only have one URL dispatcher, so make sure you choose the one that will work for all of your application’s needs.

Traversal

Object traversal is the simplest form of URL dispatcher, and is the default for Milla applications. Object traversal works by looking for path segments as object attributes, beginning with a root object until a controller is found.

For example, consider the URL http://example.org/myapp/hello. Assuming the Milla application is available at /myapp (which is controlled by the HTTP server), then the /hello portion becomes the request path. It contains only one segment, hello. Thus, an attribute called hello on the root object must be the controller that will produce a response to that request. The following code snippet will produce just such an object.

class Root(object):

    def hello(self, request):
       return 'Hello, world!'

To use this class as the root object for a Milla application, pass an instance of it to the Application constructor:

application = milla.Application(Root())

To create URL paths with multiple segments, such as /hello/world or /umbrella/corp/bio, the root object will need to have other objects corresponding to path segments as its attributes.

This example uses static methods and nested classes:

class Root(object):

    class hello(object):

        @staticmethod
        def world(request):
            return 'Hello, world!'

application = milla.Application(Root)

This example uses instance methods to create the hierarchy at runtime:

class Root(object):

    def __init__(self):
        self.umbrella = Umbrella()

class Umbrella(object):

    def __init__(self):
        self.corp = Corp()

class Corp(object):

    def bio(self, request):
        return 'T-Virus research facility'

application = milla.Application(Root())

If an attribute with the name of the next path segment cannot be found, Milla will look for a default attribute.

While the object traversal dispatch mechanism is simple, it is not very flexible. Because path segments correspond to Python object names, they must adhere to the same restrictions. This means they can only contain ASCII letters and numbers and the underscore (_) character. If you need more complex names, dynamic segments, or otherwise more control over the path mapping, you may need to use routing.

Routing

Routing offers more control of how URL paths are mapped to controller callables, but require more specific configuration.

To use routing, you need to instantiate a Router object and then populate its routing table with path-to-controller maps. This is done using the add_route() method.

def hello(request):
    return 'Hello, world!'

router = milla.dispatch.routing.Router()
router.add_route('/hello', hello)

Aft er you’ve set up a Router and populated its routing table, pass it to the Application constructor to use it in a Milla application:

application = milla.Application(router)

Using routing allows paths to contain dynamic portions which will be passed to controller callables as keyword arguments.

def hello(request, name):
    return 'Hello, {0}'.format(name)

router = milla.dispatch.routing.Router()
router.add_route('/hello/{name}', hello)

application = milla.Application(router)

In the above example, the path /hello/alice would map to the hello function, and would return the response Hello, alice when visited.

Router instances can have any number of routes in their routing table. To add more routes, simply call add_route for each path and controller combination you want to expose.

def hello(request):
    return 'Hello, world!'

def tvirus(request):
    return 'Beware of zombies'

router = milla.dispatch.routing.Router()
router.add_route('/hello', hello)
router.add_route('/hello-world', hello)
router.add_route('/umbrellacorp/tvirus', tvirus)

Controller Callables

Controller callables are where most of your application’s logic will take place. Based on the MVC pattern, controllers handle the logic of interaction between the user interface (the view) and the data (the model). In the context of a Milla-based web application, controllers take input (the HTTP request, represented by a Request object) and deliver output (the HTTP response, represented by a Response object).

Once you’ve decided which URL dispatcher you will use, it’s time to write controller callables. These can be any type of Python callable, including functions, instance methods, classmethods, or partials. Milla will automatically determine the callable type and call it appropriately for each controller callable mapped to a request path.

This example shows a controller callable as a function (using routing):

def index(request):
    return 'this is the index page'

def hello(request):
    return 'hello, world'

router = milla.dispatch.routing.Router()
router.add_route('/', index)
router.add_route('/hello', hello)
application = milla.Application(router)

This example is equivalent to the first, but shows a controller callable as a class instance (using traversal):

class Controller(object):

    def __call__(self, request):
        return 'this is the index page'

    def hello(self, request):
        return 'hello, world'

application = milla.Application(Controller())

Controller callables must take at least one argument, which will be an instance of Request representing the HTTP request that was made by the user. The Request instance wraps the WSGI environment and exposes all of the available information from the HTTP headers, including path, method name, query string variables, POST data, etc.

If you are using Routing and have routes with dynamic path segments, these segments will be passed by name as keyword arguments, so make sure your controller callables accept the same keywords.

Before and After Hooks

You can instruct Milla to perform additional operations before and after the controller callable is run. This could, for example, create a SQLAlchemy session before the controller is called and roll back any outstanding transactions after it completes.

To define the before and after hooks, create an __before__ and/or an __after__ attribute on your controller callable. These attributes should be methods that take exactly one argument: the request. For example:

def setup(request):
     request.user = 'Alice'

def teardown(request):
     del request.user

def controller(request):
    return 'Hello, {user}!'.format(user=request.user)
controller.__before__ = setup
controller.__after__ = teardown

To simplify this, Milla handles instance methods specially, by looking for the __before__ and __after__ methods on the controller callable’s class as well as itself.

class Controller(object):

    def __before__(self, request):
        request.user = 'Alice'

    def __after__(self, request):
        del request.user

    def __call__(self, request):
        return 'Hello, {user}'.format(user=request.user)

Returing a Response

Up until now, the examples have shown controller callables returning a string. This is the simplest way to return a plain HTML response; Milla will automatically send the appropriate HTTP headers for you in this case. If, however, you need to send special headers, change the content type, or stream data instead of sending a single response, you will need to return a Response object. This object contains all the properties necessary to instruct Milla on what headers to send, etc. for your response.

To create a Response instance, use the ResponseClass attribute from the request:

def controller(request):
    response = request.ResponseClass()
    response.content_type = 'text/plain'
    response.text = 'Hello, world!'
    return response

Project Versions

Table Of Contents

Previous topic

Welcome to Milla’s documentation!

Next topic

Advanced Features

This Page