The Swiss Army Knife For Python Web Developers
Werkzeug doesn’t limit you in what you do; thus there is no general rule where to locate your modules, what to do with your data etc.
However, there is a general pattern which is used in many web applications, the “Model View Controller” pattern (MVC), which makes sense for many applications.
The idea is that you have “models” in one file, the “views” in another and the “controllers” in the third file or folder. The Django framework came up with a new idea of naming those components which is called “Model Template View” (MTV) which probably makes more sense for web applications although it means nearly the same. We will use the latter naming in this file.
So here is what those terms mean:
The templates contain the actual HTML markup with placeholders for data passed to them. Although there is a minimal template language in the Werkzeug package we don’t recommend using it for larger applications. There are many good and powerful template engines out there that support debugging, template inheritance, XML output etc.
The integrated templating language is a good idea if you have one or two templates and don’t want to install one of the big template engines, but it is not suitable for more complex tasks!
There should be one central file that provides the WSGI application and dispatches the requests. It’s also a good idea to assemble the final URL map there if you use the Werkzeug routing system.
It’s also a good idea to call the file something like main.py or application.py and locate it in the root of the package. This is what it could look like:
from werkzeug import Request, Response, import_string from werkzeug.exceptions import HTTPException from werkzeug.routing import RequestRedirect from mypackage.urls import url_map def application(environ, start_response): url_adapter = url_map.bind_to_environ(environ) req = Request(environ) try: endpoint, values = url_adapter.match() view = import_string('mypackage.views.' + endpoint) resp = view(req, **values) except (RequestRedirect, HTTPException), e: resp = e return resp(environ, start_response)
You can even further simplify the dispatching by using urls.dispatch as explained in the routing documentation.
This will look for the controller functions in mypackage.views.module. If the URL is configured with an endpoint of 'static.index', the module mypackage.views.static is loaded and index(req) is called.
The URL rule parameters are passed to the function as keyword arguments then.
Note: This is just one idea of how things can look like. This doesn’t necessarily have to represent your application. You can, for example, save the request object in a thread-local storage or have your views as methods of a controller class you instantiate with the request as argument for each incoming request. You can also use other request objects or no request object at all etc.
If we continue the example above we need an utils module with the request and response objects. This would also contain other utilities and a bridge to a template engine. In this example we will use the Jinja template engine - basically because it’s something we use, too - but you can of course use any template engine:
from jinja import Environment, PackageLoader from werkzeug import Response env = Environment(loader=PackageLoader('mypackage', 'templates')) class TemplateResponse(Response): def __init__(self, template_name, **values): tmpl = env.get_template(template_name) output = tmpl.render(values) Response.__init__(self, output, mimetype='text/html')
Note: Templates in this example are saved in the templates folder inside the mypackage package. The way template loading and rendering works depends on the template engine, so have a look at it’s documentation regarding that.
We just subclass request and response classes for our needs and provide a second response subclass that is used to render templates. It’s used in the example view below.
Because we use the Werkzeug URL routing system in this example and the URLs are stored in urls.py, we need that file:
from werkzeug.routing import Map, Rule url_map = Map([ Rule('/', 'static.index') ])
This is just one small URL rule for one view.
Here is the view defined above. It must be saved in myprojects/views/static.py as defined above:
from myproject.utils import TemplateResponse def index(req): return TemplateResponse('index.html', title='Welcome')
Models and templates are out of the scope for this documentation, but you should have gotten an idea on how you can organize your WSGI application built with the help of Werkzeug.