Package: bricks
Bricks - Lightweight web application development framework

Bricks

Warning:This is a preview release of Bricks supplied with no warranty and is not production ready. The APIs are going to be changing fast so don't expect code written with this version to automatically be compatible with future versions.
Summary:
Lightweight web application development framework
Author:
James Gardner <james@jimmyg.org>
Documentation:
Not available
Licence:

Portions of this software written by James Gardner are released under the GPL.

Copyright (C) 2004 James Gardner

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Bricks is a lightweight, elegant and simple to use open-source web framework for rapidly developing real-world web applications with less code. It is similar to Python projects such as Paste and Subway and can be compared to the extremely popular Ruby on Rails.

The PythonWeb.org Modules have been re-written from the ground up to work seamlessly with Bricks so that you don't repeat yourself, can use a single language for all components. No compilation phase is required, if you make a change you can instantly see the result. Everything from templates to control flow to business logic is written in Python which also provides many useful modules for SMTP, IMAP, XML etc.

Bricks makes hard things possible and makes simple things extremely easy. It uses Web Server Gateway Interface middleware chains to simultaneously provide configuration and functionality. Bricks is compatible with any other Python WSGI component and can be easily extended or integrated with code from other projects.

Bricks is simple to install and use. Its only requirements are a recent version of Python and the latest version of easy_install .

Installation

  1. Install a recent version of Python, 2.3 or above.

  2. Get hold of the latest version of easy_install

  3. Run the following command to download and install Bricks:

    python C:\Python24\Scripts\easy_install.py bricks
    

Note: You must replace C:\Python24\Scripts with the path to where easy_install.py is on your platform. Windows users should always use / in pathnames instead of \ for parameters in Bricks applications as used in the examples in this document.

Python 2.4.0 running on Windows has a number of issues preventing Bricks from working correctly. Windows users currently using 2.4.0 should upgrade to Python 2.4.1 from http://www.python.org/2.4.1/

Getting Started

Throughout this quick tutorial you will need to replace C:\Python24\Scripts with your Python scripts directory. The tutorial has been written for Windows users because more Linux/BSD/MacOS users understand Windows than the other way around. Once we have setup a local site for development we will deploy the code in a production environment on Apache.

Create a Site

In a bricks site, files which are publically accessible on the server, such as pictures and HTML documents are kept in a public directory and all application code and scripts are kept in a private directory and cannot be directly accessed via a web browser. To create a site you must specify both a public and private directory.

Windows 2000/XP users: Bring up a command prompt by clicking Start then Run. Type cmd and press enter.

Create a new installation in C:\test like this:

python C:\Python24\scripts\bricks --auto C:/test

This will automatically create all the directories you need and install all the files. If you need more control, perhaps if your directories already exist and you need to install Bricks to them, you can use this command:

python C:\Python24\scripts\bricks -v C:/test/private -u C:/test/public 

but you will need to ensure the C:/test/private and C:/test/public directories already exist.

Bricks tries to guess the correct path to Python but you may need to edit the first line of the files bricks.py and bricks.py.fcgi. The second of these scripts is for FastCGI execution of Bricks, the first for normal CGI execution. You can set the variables at the top of each to determine if debug output should be produced in the event that an error occurs and is not picked up by error handling middleware.

Start the server

Your new test site comes with a webserver configured to be serve files, scripts and applications from your site.

Start the server as follows:

python C:\test\private\script\server.py -d -p 8000

The -d means display full debug information if an error occurs. The -p 8000 means run the server on port 8000. If you visit http://localhost:8000 you will be able to see the default welcome page which at the moment isn't too exciting.

The full list of options available can be obtained by running:

python C:\test\private\script\server.py -h

If you are running a production server you would probably want to run the server on port 80 without debug information:

python C:\test\private\script\server.py

There is also a script named debug.bat which you can double click under Windows to start the server with the default debug options.

Create an application

Application names must start with a letter and contain only letters, numbers and the _ character. Applications cannot be named cgi-bin (which would confuse with the public cgi-bin directory) or auth (which would conflict with the authorisation system's use of the session store) and should not be the same as any directory name in the C:\test\public directory. Other than that they can be called just about anything. We are going to call our application todo since it will be a todo list manager.

We make the application as follows:

python C:\test\private\script\app.py crud todo

The crud argument tells app.py that this is a crud application and to automatically create a model, contoller and view named todo. CRUD stands for Create Read Update Delete and is a silly way of describing something which handles all the common things you would do to a database table.

Note: At the moment if you call your application anything other than todo you will need to manually edit the files themselves so they are called the right thing. This will be fixed once a formal plugin mechanism has been decided upon.

Once you have created the skeleton todo application you will need to define the model todo will use and then configure the database connection as descibed next.

Define the Model

Bricks uses a Model Controller View architecture. When we created the application, a controller and view were automatically created and a skeleton model was made. The model doesn't yet contain any values so we need to define the field types needed in the database table to hold the model information.

Our todo model is going to have a description string for writing the todo entry and a true or false value for whether the item has been completed or not.

Open a text editor such as Notepad and customise C:\test\private\app\todo\model\todo.py so that it looks like this and save it:

import database.object

todo = database.object.Table(name='Todo')
todo.add(field='String', name='description', required=1)

table = todo

As you can see we have added two fields to the Todo table to provide us with the functonality we need. Both fields are required, which means that a value must be specified and that the database table cannot take a NULL value for either field.

Warning: Once you have created the model, configured the database connection and started the application the model will be created in the database. If you then change the model, the changes will not be updated in the database and your program will behave unpredictable or more likely crash. If you need to change the model at a later date you will need to drop the corresponding table in the database first.

Configure the Database Connection

By default, Bricks uses Engine as the database engine. The todo application is already configured to use Engine, however Engine is not really suitable for production use so you can also specify your own database connection settings instead.

In C:\test\private\app\todo\local.py change the following lines so that they use the settings for the database you wish to use. The defaults work fine but you might want to use a different database engine. You can do so like this:

# Database Settings 
adapter='sqlite',
database='data.db',
autoCreate = 1

Or to connect to a MySQL database you might use:

# Database Settings
adapter='mysql', 
database='name',
host='mysql.example.com',
user='foo',
password='bar'

Parameters you might wish to use include host, user, password, port and database. The autoCreate parameter means that the database table will be automatically created for you if it doesn't already exist.

Note: Bricks currently only supports Engine, SQLite and MySQL. Engine is supported automatically but if you wish to use MySQL or SQLite you will also need to install the Python modules MySQLdb or pysqlite respectively.

Test the Application

We have now got everything in place to test our todo application. Start the server as described earlier:

python C:\test\private\script\server.py -d -p 8000

and visit http://localhost:8000/todo to see it in action.

If you make changes to the todo application model or controller code they will not be noticed until you restart the server becuase the server loads them once and then runs them on every request without reloading them to make the execution much faster.

This is great for a production server but not so good for testing. Luckily you can test your application running as a CGI script instead. Visit http://localhost:8000/cgi-bin/bricks.py/todo

In CGI mode the application is reloaded on every request so that if you change the code, the changes will be immediately visible but execution is slower.

You should now have a complete set of tools for adding/editing/removing objects from the database. Have a play with the interface. The beauty of this is that if you had defined different entries for the model, the forms would all have been different to suit the model you defined. This takes a lot of the work out of most programming. Note also that you can't yet use fields with one to many or many to many mappings, just straightforward tables.

You can add new controllers for different models by copying and appropriately editing the existing controller, model and views for the todo controller. You don't need to, and indeed shouldn't, create a new application when just adding a new controller would do.

Customise the View

The todo application is fully functional but not terribly pretty. Fortuneatly you can easily customise the view by editing the view templates in C:\test\private\app\todo\view\todo. For example, change crud_new.html to the following to make the background for the new entry form yellow:

<html>
<head>
<title>New Entry</title>
</head>
<body bgcolor=&quot;#ffffcc&quot;>
<h1>New Entry</h1>
$form
</body>
</html>

to make the background of the page yellow.

The views use Cheetah for the templating. You can read more about Cheetah in the Cheetah reference manual. Packaging the Application

Summary

You should now be able to write your own basic database applications using Bricks. Try creating a basic address book application. You will not need to reconfigure the database, the template or go through the process of setting up bricks becuase you can add your addressbook to the existing site created and configured for the todo application.

Customising the Controller

We have already defined our model and customised our view. The controller is the code that provides the glue between the model and the view. When we created the application a suitable contoller was automatically provided.

The controller in C:testprivateapptodocontrollerindex.py currently looks like this:

from bricks.controller.crud import CRUD

class Test(CRUD):
   pass

controller = Test('todo')

Our controller is named Test and derived from one of the Bricks standard controllers, namely bricks.controllers.crud.CRUD. This means it has all the functionality of the CRUD controller. CRUD stands for Create, Read, Update and Delete which is exaclty what this controller does to the model. Bricks looks for an object named controller in every controller file so that it knows which object is the controller. The last line just says that an instance of our Test class, linked to the model named todo which we created earlier, is our controller.

Controllers contain four types of methods, public methods, private methods, test methods and methods which require authorisation. You can set the type of method by giving the method's expose attribute a value of True, False or setting authorisation permissions.

XXX More about testing

Adding Authorisation Code

If we wanted to only allow signed in users to access the delete method we could use the following code:

from bricks.controller.crud import CRUD
from bricks import user

class Test(CRUD): 
    
    # Override the default delete() method
    def delete(self, rowid):
        return CRUD.delete(self, rowid)
    delete.expose = user(level=1, app='app')

controller = Test('todo')

This would automatically present a sign in form to the user if they tried to sign in. Try signing in as john with the password bananas. This user is automatically setup when auth middleware is created.

The sign in functionality, however, requires that some additional middleware components are added to the middleware chain. There is also an auth skeleton application which includes the auth middleware components. See the next section.

We need to specify app='app' in the user() function because otherwise the auth system uses the default application name todo and the sample user only has access to the application app. If we tell Bricks that we want to give access to any user of the application app with an access level of 1 then the sample user will be able to use delete methods which is what we want in this instance. Of course in a real application you would add users to the auth system for the application todo so this wouldn't be a problem.

We might also want them to be able to sign out by visiting /todo/todo/signout so we can add this method:

def signout(self):
    if self.environ.has_key('web.auth.user'):
        self.environ['web.auth'].signOut()
        self.environ['database.connection'].commit()
        return ['Signed Out']
    else:
        return ['You aren't signed in']
signout.expose = 1

Of course you might like to format the messages into a nice HTML page using self.view instead if just displaying strings.

Adding Other Types of Applications

As well as a skeleton crud application there are also bricks and wsgi skeletons.

The WSGI skeleton just installs a Hello World! generic WSGI application which makes no use of Bricks' advanced features. This is a good starting point if you want to use Bricks as a lightwight system for writing your own WSGI applications. You can create a WSGI skeleton applicaiton named hello like this:

python C:\test\private\script\app.py wsgi hello

The bricks example is a good starting point for a Bricks application and demonstrates Bricks' auth capabilities. You will need to configure a database connection as described earlier. Create an auth application named secret:

python C:\test\private\script\app.py bricks secret

Note: This may change in a future release where applications will be generic but you add custom controllers to each application.

Warning: By default, when the auth tables are set up in the database a user with the username john, password bananas and access level of 1 is set up too so that you can test the skeletons. Be aware that this will happen in your own applications too and poses a security threat if you don't realise the user is there!

Other points

By default the template used is in C:\test\private\template and the logfile used is in C:\test\private\log. Databases files are stored in C:\test\private\data. Bricks is designed so that you can customise everything should you wish to, by directly editing the code. We will customise the database connection for our todo application later in the tutorial but all other options can be customised in a similar way.

Bricks is designed so that many different applications can run on the same site and share common templates and database connections. Now that we have an environment running for the site we can create such an application.

There is also one other very useful paramter you can use named prepend. This is a word which is transparently added to the name of every table you create so that you can install multiple bricks applications into the same database. This is useful if two applications both use a table with the same name but you only have one database on your web hosting account. You could give one application the prepend 'app1' and the other 'app2' and then they would both be able to use the same database without conflicting. This can also be used if you want to have a production and a test version of the code.

Production Deployment with FastCGI

If you wish to use FastCGI with Bricks you can expect a 5 times performance improvement compared to CGI for most typical applications. First make sure mod_fastCGI is installed on Apache and enabled in the httpd.conf file. In your .htaccess add the following lines for each application you want to run with FastCGI.

Instead of using bricks.py to run your Bricks applications, bricks.py.fcgi will be used and mod_rewrite will be used to make the URLs look prettier. Add the following lines to the .htaccess file in the C:\test\public directory:

# Enable this rewrite rule to point to the controller/action that should serve root.
RewriteEngine On
RewriteRule   ^books/([-_a-zA-Z0-9/]+)$  /cgi-bin/bricks.py.fcgi/wsirn5/$1

# Make apache realise that the bricks.py.fcgi script should be used with FastCGI
AddHandler fastcgi-script .fcgi

XXX More detail here needed including the drawbacks of FastCGI, how to get URLs generated by bricks to use the mod_rewrite url and how ti install FastCGI in the first place!

Troubleshooting

Sessions don't seem to be saving

If you are using session or auth middleware and finding that sessions are being re-created each time and auth sign ins go in an infinite loop of redirects, particularly if you are using a new database when you used to use MySQL it is because you have forgotton to commit() databaase changes as the first step in your controller:

def setup(self):
    self.environ['database.connection'].commit()
    # Other initialisation code can go here

Some versions of MySQL do not have transaction support so commit automatically but all other databases need an explicit commit()

Once you've made this change you will need to clear your browser's cookies or wait for them to expire because lots of invalid SessionIDs have probably been set.

XXX Something here about i18n using gettext

Sign In Form redirects me to the cgi-bin version of the site

If you are using Apache and mod_rewrite in a production environment you will notice that when you sign in you are taken to the full URL of the bricks component rather than the re-written one. This is because Apache sends the full URL information to Bricks through environmental variables and so when the auth middleware initially logs the URL you are trying to access, it logs the full URL and therefore redirects to it once you are signed in. The short RL will still work perfectly well. A workaround will be introduced in a future version.

Future

Now that Bricks works, effort will be concentrated on making it easier to use, providing more and better skeleton applications, controllers etc. A management section will be introduced and a plugin system devised so that it can be user-extended. All very exciting!

API Reference

Modules

bricks.controller

Controller Classes

bricks.tools

makesite.py -d &quot;adapter='sqlite', database='data.db'&quot; C:/test2/public C:/test2/private2

Module Attributes

__file__ = 'C:\\Documents and Settings\\James\\Desktop\\PythonWeb\\bricks\\bricks\\__init__.py'

__name__ = 'bricks'

__path__ = ['C:\\Documents and Settings\\James\\Desktop\\PythonWeb\\bricks\\bricks']

date = '2005-02-15'

name = 'Bricks'

status = 'beta'

version = '0.6.0pre0'

version_info = (0, 6, 0, 'pre0', 'beta')

Functions

def getControllerAndAdaptURL(environ)
[show source]
C:\Documents and Settings\James\Desktop\PythonWeb\bricks\bricks\__init__.py

  641: def getControllerAndAdaptURL(environ):
  642: 
  643:     path = environ['bricks.path'].split('/')
  644:     if len(path) > 0 and path[0] <> '':
  645:         #environ['bricks.url'] += '/'+path[0]
  646:         environ['bricks.path'] = '/'.join(path[1:])
  647:         moduleName = path[0]
  648:     else:
  649:         moduleName = 'index'
  650:     try:
  651:         environ['bricks.controller'] = moduleName
  652:         environ['bricks.controller.url'] = environ['bricks.app.url']+'/'+moduleName
  653:         #"Return the current URL that the user should type to get to the same page. This is not the same as the internal URL which the user might have been redirected to where Bricks actually runs"
  654:         environ['bricks.url'] = environ['bricks.controller.url']+'/'+environ['bricks.path']
  655:         if environ.has_key('QUERY_STRING'):
  656:             environ['bricks.url']=environ['bricks.url']+'?'+environ['QUERY_STRING']
  657:         module = environ['bricks.import'](os.path.join(environ['bricks.private'],'app',environ['bricks.app'],'controller',moduleName))
  658:     except ImportError:
  659:         from web.wsgi.exception import HTTPError404
  660:         raise HTTPError404("No such controller %s"%repr(moduleName))
  661:     if not module.__dict__.has_key('controller'):
  662:         raise Exception('No WSGI controller object found in the %s controller'%repr(moduleName))
  663:     return module.controller

def loadModel(environ)

Not used yet

[show source]
C:\Documents and Settings\James\Desktop\PythonWeb\bricks\bricks\__init__.py

  594: def loadModel(environ):
  595:     "Not used yet"
  596:     # Load the models, check the tables exist and the columns exist and are correct types.
  597:     return None

def user(**authParams)

Returns a list of parameters to specify which signed in users should be allowed access to the method. The function takes any of the arguments of the AuthUser.authorise() method from the web.auth module.

Used in a controller class as follows:

import bricks.user

def secret(self):
    yield 'This is a secret message'
secret.expose = user(role='spy')
[show source]
C:\Documents and Settings\James\Desktop\PythonWeb\bricks\bricks\__init__.py

  579: def user(**authParams):
  580:     """Returns a list of parameters to specify which signed in users should be allowed access to the method.
  581:     The function takes any of the arguments of the ``AuthUser.authorise()`` method from the ``web.auth`` module.
  582: 
  583:     Used in a controller class as follows::
  584: 
  585:         import bricks.user
  586: 
  587:         def secret(self):
  588:             yield 'This is a secret message'
  589:         secret.expose = user(role='spy')
  590: 
  591:     """
  592:     return authParams

Classes

class View

Used in controller classes as self.view to render views in the application's view directory.

Used as follows in a controller class:

def index(self):
    yield self.view(
        'index.template',
        {
            'content':'Hello World!',
        }
    )
def __call__(self, file, namespace={}, format='Cheetah')

file is always the first parameter, namespace will always be the second parameter

[
show source]
C:\Documents and Settings\James\Desktop\PythonWeb\bricks\bricks\__init__.py

  617: def __call__(self, file, namespace={},format='Cheetah', ):
  618:     "``file`` is always the first parameter, ``namespace`` will always be the second parameter"
  619:     if namespace.has_key('environ'):
  620:         raise Exception("The key 'environ' is reserved")
  621:     # Copy namespace and join lists
  622:     n = {}
  623:     for k, v in namespace.items():
  624:         n[k] = v
  625:     n['environ'] = self.environ
  626:     # Check the minimum requirements are there
  627:     for key in ['title','head','content']:
  628:         if not n.has_key(key):
  629:             n[key] = ''
  630:     realFile = os.path.abspath(os.path.join(self.environ['bricks.private'],'app',self.environ['bricks.app'],'view',file))
  631:     if not os.path.exists(realFile):
  632:         raise Exception('No such view %s'%repr(realFile))
  633: 
  634:     result = web.template.parse(
  635:         file=realFile, 
  636:         type=format,
  637:         dict=n,
  638:     )
  639:     return result

def __init__(self, environ)
[show source]
C:\Documents and Settings\James\Desktop\PythonWeb\bricks\bricks\__init__.py

  614: def __init__(self, environ):
  615:     self.environ = environ