Warning
This is a work in progress and hasn't been tested yet
In this tutorial we will create a simple website with a main template, top-level navigation, a breadcrumb trail and some simple components.
The 3aims website runs on a Pylons application which is very similar to this, as does the PylonsHQ site.
| Note: | This tutorial uses Pylons 0.9, the development version of Pylons. |
First install Pylons 0.9:
easy_install -U http://pylonshq.com/svn/Pylons/trunk
When Pylons 0.9 is released you should install it like this instead:
easy_install -U pylons==0.9
Then create your project:
paster create --template=pylons website
Start up the development server to see what we already have:
cd website
paster serve --reload development.ini
Visit http://localhost:5000
If you look at your config/routing.py file you will see this line last:
map.connect('*url', controller='template', action='view')
If none of the other routes match the request URL the request will be dispatched to the view method of the template controller.
If you look at the template controller you will see that the view() method simply returns a 404 response:
abort(404)
This is usually what you would want, if the file cannot be found a 404 error should be raised. We are going to change this method so that Pylons checks if a suitable template can be found before raising a 404.
Our application needs to check whether or not the URL requested can be found as a file in the templates directory. Luckily Pylons applications use the Python Eggs format so we can use pkg_resources to see if the template exists. See http://peak.telecommunity.com/DevCenter/PkgResources#basic-resource-access for full information.
Before pkg_resources can find our application we need to install it. In the project directory run this command:
python setup.py develop
This command installs the application in a development mode so that changes we make to the code are instantly available in the website package.
We can now write the code to inspect our website package to see if the template resource exists in our applications. Add this line to the top of the template.py controller to look like this:
import pkg_resources
then add this code to the view() method:
page = '/'.join(['templates',url+'.myt'])
if pkg_resources.resource_exists('website', page) \
and not pkg_resources.resource_isdir('website', page):
# avoid unicode errors with str(...)
return render_response(str(url+'.myt'))
directory = '/'.join(['templates',url])
if pkg_resources.resource_isdir('website', directory) \
and pkg_resources.resource_exists('website', directory+'/index.myt') \
and not pkg_resources.resource_isdir('website', directory+'/index.myt'):
return render_response(str(url.strip('/')+'/index.myt'))
abort(404)
Note that if you called your project something other than website you would need to adjust the code above accordingly.
Create a sample template named templates/test.myt which contains:
Hello World!
Then visit http://localhost:5000/test and you will see your Hello World! message displayed.
The code we wrote in templates.py will also serve an index.myt file if a directory is requested. Create a new file in the templates directory called index.myt and add the text:
This is the site homepage.
If you visit http://localhost:5000/ you will notice the old welcome message is still there. This is because the default configuration of the Cascade middleware in config/middleware is to serve static files before trying to serve files from the Pylons application.
Delete public/index.html and visit http://localhost:5000/ again. This time you should see your index page displayed.
For this simple website we are going to take advantage of Myghty template inheritance and autohandlers. http://www.myghty.org/docs/inheritance.myt Edit the autohandler file in the templates directory to look like this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title><& SELF:title &></title>
</head>
<body>
<& /navbar.myc, toggle=m.scomp('SELF:nav') &>
% m.call_next()
</body>
</html>
<%method title>
Simple Website
</%method>
<%method nav>Home</%method>
| Note: | There should be no leading spaces before the line % m.call_next() or the inheritance will not work. |
You should also create a file called navbar.myc in the templates directory which contains the following:
<p>
% for link, url in [('Home','/'), ('Test','test')]:
% if ARGS['toggle'] == link:
<a href="<% url %>"><b><% link %></b></a>
% else:
<a href="<% url %>"><% link %></a>
% #endif
% #endfor
</p>
Again, there should be no leading space before any of the % signs.
Now if you visit http://localhost:5000/test again you will see the navigation bar in place automatically because Myghty calles the autohandler first and then the m.call_next() code calls the test.myt file and inserts the content in its place.
In this way it is possible to create a heirachy of templates each of which add more HTML as you drill down through directories.
It would be nice if the title could change in each page. Try adding this to the test.myt file at the bottom:
<%method title>
Hello Test Page
</%method>
If you visit the test page again the title will have changed. The line <& SELF:title &> we defined earlier in templates/autohandler calls the title() method of any child templates. In this case because we have defined a title method the title of the page is replaced by the value in that method, in this case, Hello Test Page.
Now try adding this instead:
<%method title>
<& PARENT:title &> : Hello Test Page
</%method>
This time when you visit http://localhost:5000/test the tite contains the title of the page above it in the hierarchy and also `` : Hello Test Page`` added to the end. This is because <& PARENT:title &> is replaced with the value of the title() method on the parent template, in this case the autohandler file.
In a similar way to the way we can modify the title in the child template we can also change which navigation link is selected. Add this to the test.myt file at the bottom:
<%method nav>Test</%method>
You should now find when you visit http://localhost:5000/test that the Test link is highlighted not the Home link. This is because the navbar.myc component is passed the value of the nav() method as the argument toggle. Since we have now defined the nav() method a different value is displyed.
Note that in the title() method we could put the text on multiple lines but in the nav() method defined above it all has to be on one line. This is because the extra whitespace isn't a problem between <title></title> tags but is a problem when comparing strings in navbar.myc.
We can add in a breadcumb trail in a very similar way to how we achieved the title. Add this to templates/autohandler:
<%method breadcrumbs>
% return [['Home','/']]
</%method>
Then add this to test.myt at the bottom:
<%method breadcrumbs>
% a = m.comp('PARENT:breadcrumbs')
% a.append(['Hello Test Page','/test'])
% return a
</%method>
Next we need another component to render the breadcrumb trail. Create a new component templates/breadcrumbs.myc:
<p>
% counter = 0
% for link, url in ARGS['breadcrumbs']:
% counter += 1
% if counter == len(ARGS['breadcrumbs']):
<a href="<% url %>"><b><% link %></b></a>
% else:
<a href="<% url %>"><% link %></a> >
% #endif
% #endfor
</p>
Finally we need to place the breadcrumbs into autohandler. Add the following line just above % m.call_next():
<& /breadcrumbs.myc, breadcrumbs=m.comp('SELF:breadcrumbs')&>
Visit http://localhost:5000/test again and you will see a simple breadcrumb trail.
That's it! You now have a good working basis for a website without going near a Pylons controller!