Web applications include many static assets in the UX: CSS and JS files, images, etc. Web frameworks need to support productive development by the UX team, but also the richness and complexity required by the core developers and the deployment team.
It’s a surprisingly hard problem, supporting all these needs while keeping the simple case easy.
Pyramid accomplishes this using the view machinery and static assets.
Again, let’s use the previous package as a starting point for a new distribution, plus make a new directory for the static assets:
(env33)$ cd ..; cp -r step07 step08; cd step08
(env33)$ mkdir tutorial/static
(env33)$ python3.3 setup.py develop
The Configurator needs to be told in tutorial/__init__.py about the static files by calling its add_static_view:
1 2 3 4 5 6 7 8 9 10 11 12 13 from pyramid.config import Configurator def main(global_config, **settings): config = Configurator(settings=settings) config.add_route('wiki_view', '/') config.add_route('wikipage_add', '/add') config.add_route('wikipage_view', '/{uid}') config.add_route('wikipage_edit', '/{uid}/edit') config.add_route('wikipage_delete', '/{uid}/delete') config.add_static_view(name='static', path='tutorial:static') config.scan() return config.make_wsgi_app()
Our tutorial/views.py has some extras with dummy data driving the listing and viewing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | from pyramid.httpexceptions import HTTPFound
from pyramid.renderers import get_renderer
from pyramid.view import view_config
pages = [
dict(uid='100', title='Page 100', body='<em>100</em>'),
dict(uid='101', title='Page 101', body='<em>101</em>'),
dict(uid='102', title='Page 102', body='<em>102</em>'),
]
class WikiViews(object):
def __init__(self, request):
self.request = request
renderer = get_renderer("templates/layout.pt")
self.layout = renderer.implementation().macros['layout']
def get_pages(self):
return pages
@view_config(route_name='wiki_view',
renderer='templates/wiki_view.pt')
def wiki_view(self):
return dict(title='Welcome to the Wiki', pages=pages)
@view_config(route_name='wikipage_add',
renderer='templates/wikipage_addedit.pt')
def wikipage_add(self):
return dict(title='Add Wiki Page')
@view_config(route_name='wikipage_view',
renderer='templates/wikipage_view.pt')
def wikipage_view(self):
uid = self.request.matchdict['uid']
page = [page for page in pages if page['uid'] == uid][0]
title = page['title']
return dict(page=page, title=title)
@view_config(route_name='wikipage_edit',
renderer='templates/wikipage_addedit.pt')
def wikipage_edit(self):
uid = self.request.matchdict['uid']
page = [page for page in pages if page['uid'] == uid][0]
title = 'Edit ' + page['title']
return dict(title=title)
@view_config(route_name='wikipage_delete')
def wikipage_delete(self):
url = self.request.route_url('wiki_view')
return HTTPFound(url)
|
Make a tutorial/static/wiki.css for the styling:
body {
font-family: sans-serif;
margin: 2em;
}
h1 a {
vertical-align: bottom;
}
We also have a logo PNG file that we need saved at tutorial/static/logo.png. Click this link to download and save the image.
The tutorial/templates/layout.pt includes the CSS on all pages, plus adds a logo that goes back to the wiki home:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:tal="http://xml.zope.org/namespaces/tal"
metal:define-macro="layout">
<head>
<title>Wiki - ${title}</title>
<link rel="stylesheet"
href="${request.static_url('tutorial:static/wiki.css')}"/>
</head>
<body>
<div id="main">
<h1>
<a href="${request.route_url('wiki_view')}">
<img src="${request.static_url('tutorial:static/logo.png')}"
alt="Logo"/></a>
${title}</h1>
<div metal:define-slot="content">
</div>
</div>
</body>
</html>
|
tutorial/templates/wiki_view.pt switches to a more-general syntax for computing URLs, plus iterates over the actual (dummy) data for listing each wiki page:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <div metal:use-macro="view.layout">
<div metal:fill-slot="content">
<a href="${request.route_url('wikipage_add')}">Add
WikiPage</a>
<ul>
<li tal:repeat="page pages">
<a href="${request.route_url('wikipage_view', uid=page.uid)}">
${page.title}
</a>
</li>
</ul>
</div>
</div>
|
We also change tutorial/templates/wikipage_view.pt to use the route_url approach to URLs:
1 2 3 4 5 6 7 8 9 10 11 12 | <div metal:use-macro="view.layout">
<div metal:fill-slot="content">
<a href="${request.route_url('wikipage_edit', uid=page.uid)}">
Edit
</a> |
<a href="${request.route_url('wikipage_delete', uid=page.uid)}">
Delete
</a>
<p>${structure: page.body}</p>
</div>
</div>
|
Run the tests in your package using nose:
(env33)$ nosetests .
..
-----------------------------------------------------------------
Ran 2 tests in 1.971s
OK
Run the WSGI application:
(env33)$ pserve development.ini --reload
Open http://127.0.0.1:6547/ in your browser.
We made files and directories in tutorial/static available at the URL static. However, we used tutorial:static as the argument in add_static_view. Pyramid uses a robust scheme called asset specifications to work with static assets.
In our templates, we resolved the full path to a static asset in a package by using request.static_url and passing in an asset specification. route_url, static_url, and friends let you refactor your URL structure, or even publish to a different root URL, without breaking the links in your templates.
Finally, we’re cheating by having mutable dummy data at module scope. We will replace this shortly with database-driven data.