Pylons 0.9.6 Cheat Sheet
This cheat sheet is a very condensed form of documentation for the Pylons web framework. It is no replacement for the official documentation and not useful for learning it. Merely to look up details.
This cheat sheet is outdated because there is a newer Pylons version available.
Links
Installation
- Debian/Ubuntu
- Install: aptitude install python-pylons (available in lenny/testing or sid/unstable but not Etch!)
- Update: aptitude update && aptitude install python-pylons
- Easy-Install:
- Install: easy_install Pylons
- Update: easy_install -U Pylons
- virtualenv
Project (Paste)
- Create new project
- paster create -t pylons myapplicationsname
- Remove the public/index.html welcome page
- Customize the routing in config/routing.py (e.g. set '' to a start controller)
- Update project to new Pylons version: paster create -t pylons myapplicationsname (from above the directory where your development.ini is located)
- Serve application via HTTP: paster serve --reload development.ini (application runs at http://localhost:5000)
- Interactive shell: paster shell (install ipython for additional convenience)
- Gory details of Pylons execution
development.ini
- [server:main] (Paste)
- use: points to egg of Paste's web server
- host: the IP address the web server listens on
- port: the TCP port the web server listens on
- [app:main] (Pylons)
- use: points to your application
- full_stack: enables (by default) the error and exeption handling
- cache_dir: directory where cached HTML templates and cookie-sessions are saved
(%(here)s refers the project's root directory (where the development.ini lives))
-
Accessing settings from the [app:main] section:
from pylons import config
my_setting = config['foo.bar']
Directory structure
data/sessions
Cookie-based sessions are saved into files in this directory.
data/templates
Cached HTML files that are rendered from your template files are stored here.
development.ini
The startup configuration for Paste.
myapplication/config/
Global configuration files of your project. Used to define routes for URL dispatching, middleware (like for adding authentication) or the template system you prefer.
myapplication/controllers/
The location of the classes that contain your application logic. This code controls your application, renders templates, and queries the database.
myapplication/docs/
Put any documentation on the application here. Preferably in rest (restructured text) format.
myapplication/i18n/
Files that deal with localized message strings of your project. (i18n = internationalization)
myapplication/lib/
Contains files that set up a number of global variables and objects that you can use in your controllers. For example everything in lib/base.py is made available in every controller.
myapplication/model/
Here go your database models. They define the database schema and sets up ORM mappers.
myapplication/public/
Static files like images, CSS style sheets or Javascript files may go here.
myapplication/templates/
Your templates that render the actual HTML output are saved here.
myapplication/tests/
Every controller you create gets a counterpart to implement automated tests here.
README.txt
Some basic instructions that you can give to the admininstrator who is supposed to install your application.
ez_setup, myapplication.egg-info, setup.cfg, setup.py
Administrative files that are used to create an egg from your project that can be deployed on a web server then.
test.ini
Similar to the development.ini. This file is used when you want to run automated tests on your project.
Controllers
- Create new controller: paster controller name-of-new-controller
- Controller mycontroller is located in controllers/mycontroller.py as class MycontrollerController
- The index method is called when no action is specified
- All symbols from lib/base.py are imported
-
Routes parameters can be accepted as arguments:
def index(self, id):
return 'Your ID is ' + id - Other parameters are available through request.params['my_paramter']
-
Return values
- A (unicode) string
- Rending a Mako template: render('/mytemplate')
- Sending a HTTP error code: abort(404)
- An iterator (StreamingController example)
- Redirecting to another URL: redirect_to(controller='start', action='about') or redirect_to('/start/about')
-
Modifying the reponse object (to be done before the action's return statement:
response.headers['content-type'] = 'text/xml; charset=utf-8'
response.set_cookie('sitelang', 'uk')
response.status_code = 201
Global objects
Configuration data is available through the config object
- Variables from the app:main section of the development.ini: config['app_conf'] dictionary
- Variables from the [DEFAULT] section of the development.ini: config['global_conf'] dictionary
- Absolute path to the ini file: config['__file__']
- Name of the application: config['package'] or config['pylons.package']
- Path to the current project: config['here']
- The 'h' (webhelpers) object: config['pylons.h'] (set up in config/environment.py)
- The 'g' (globals) object: config['pylons.g'] (set up in config/environment.py)
- Path configuration (where controllers, templates and static files are located): config['pylons.paths'] (set up in config/environment.py)
Routes
- Links
- Defined in config/routing.py
- Start controller (for path /) can be specified as map.connect('', controller='start')
- map.connect('newstoday', controller='news') -> calls the ''class NewsController'' in the controllers/news.py file.
- Additional arguments are passed to the controller's method as named arguments (e.g. def __index__(self, id, name, city):)
Mako (Templates)
- Unicode
- Start all template with: # -*- coding: utf-8 -*-
- Comments start with '##' - those lines are not rendered
- Commands (do not forget the trailing ':')
- % for / % endfor
- % if / % elif / % else / % endif
- Printing variables
- ${ c.variablename }
- The closing bracket must not be used alone on a line
Sample base template (to be inherited)
# -*- coding: utf-8 -*-
<?xml version="1.0" encoding="UTF-8"?>
<!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" lang="en" xml:lang="en">
<head>
<title>...........</title>
${ h.stylesheet_link_tag( '/style1.css', '/style2.css') }
${ h.javascript_include_tag( 'jquery.js', 'jquery.debug.js') }
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
</head>
<body>
<h1>My application</h1>
${ next.body() }
</body>
</html>
Inheriting from base template
# -*- coding: utf-8 -*-
<%inherit file="master.mako"/>
<p>Hello World</p>
Database access
- Documentation links
- pylons.database and SAContext are deprecated!
- See Using SQLAlchemy with Pylons.
-
development.ini:
sqlalchemy.url = sqlite:///%(here)s/phonebook.db
sqlalchemy.url = mysql://username:password@host:3306/database
sqlalchemy.url = postgres://username:password@host:5432/database
sqlalchemy.url = oracle://username:password@host:1521/sidname -
Options
-
MySQL closes inactive connections automatically. You would need:
sqlalchemy.pool_recycle = 3600
-
Print all queries to the console (you can as well use the 'logging' module as described in the SQLAlchemy documentation):
sqlalchemy.echo = True
- Complete list of supported options
-
MySQL closes inactive connections automatically. You would need:
-
Defining tables and ORM-mapping in model/__init__.py
-
Simple mapping:
import sqlalchemy as sql
import sqlalchemy.orm as ormmetadata = MetaData()
# Define table
my_table = sql.Table(
'tablename', metadata,
sql.Column('id', sql.Integer, primary_key=True), # gets a sequence/serial assigned
sql.Column('othertable_id', sql.Integer, sql.ForeignKey("othertable.id")),
sql.Column('name', sql.Unicode(80), nullable=False, unique=True)
)# Define object class for ORM-mapping (every object matches one row)
class MyModel(object):
def __repr__(self):
return "MyModel (%s)" % self.nameorm.mapper(MyModel, my_table)
-
Simple mapping:
- Test access to the mapped models comfortably using paster shell
-
Creating queries and getting results
- Get one row with certain field content (raises an exception if there are more rows returned): model.Session.query(model.MyModel).filter_by(name='John').one()
- Get an iterator of items matching certain criteria: model.Session.query(model.MyModel).filter(model.MyModel.name=='John')
- Get an iterator of items having certain field content: model.Session.query(model.MyModel).filter_by(name='John')
- Get an iterator of items selected by a custom SQL condition: model.Session.query(model.MyModel).filter_by("id<:value and name=:name").params(value=224, name='fred')
- Get one row with primary key 42: model.Session.query(model.MyModel).get(42)
- Get all rows: model.Session.query(model.MyModel).all()
- Get the first row: model.Session.query(model.MyModel).first()
- Get the single row (raises an exception if there are more rows returned): model.Session.query(model.MyModel).one()
-
Get a limited number of rows (as a list)
- model.Session.query(model.MyModel).offset(50).limit(10).all()
- model.Session.query(model.MyModel)[10:50].all()
-
Order rows
- model.Session.query(model.MyModel).order_by(model.MyModel.age).all()
- model.Session.query(model.MyModel).order_by(model.sql.desc(model.MyModel.age)).all()
- model.Session.query(model.MyModel).order_by([model.MyModel.age, model.MyModel.city]).all()
- Count rows in result: model.Session.query(model.MyModel).count()
- Joins (beware of cartesian products): model.Session.query(model.Model1, model.Model2).all()
-
Use filter and filter_by for generative queries:
query = model.Session.query(model.MyModel)
query = query.filter_by(name='John', type=189)
query = query.filter_by(city='Hamburg')
results = query.all() -
Logical operators
- AND: and_(condition1, condition2, ...)
- AND: (condition1 & condition2 & ...)
- OR: or_(condition1, condition2, ...)
- OR: (condition1 | condition2 | ...)
- NOT: not_(condition)
- NOT: ~(condition)
-
Query operators:
- ==
-
<
- >
- <=
- >=
- startswith('foo')
- endswith('.com')
- like('%jean')
- between(100,500)
- in('A', 'CNAME', 'MX')
- + (concatenation of strings)
- op('...') (custom operator)
- ==None (NULL comparison)
-
Query functions:
- Gen eral syntax: func.FUNCTIONNAME(...)
- e.g. func.count() or func.now()
-
Creating new objects:
new_object = model.MyModel()
new_object.name = 'Jane'
new_object.city = 'Tokio'
model.Session.save(new_object)
model.Session.flush() # not needed if set autoflush=True
model.Session.commit() # needed because SQLAlchemy 0.4 uses transactions everywhere -
Altering objects:
old_object = model.Session.query(model.MyModel).get(42)
old_object.city = 'Paris'
model.Session.flush() # not needed if set autoflush=True
model.Session.commit() # needed because SQLAlchemy 0.4 uses transactions everywhere -
Deleting objects (do not use del(...)):
old_object = model.sac.query(model.MyModel).get(42)
model.Session.delete(old_object)
model.Session.flush() # not needed if set autoflush=True
model.Session.commit() # needed because SQLAlchemy 0.4 uses transactions everywhere -
Arbitrary selects (return what you query for instead of complete rows):
query = model.sql.select([model.MyModel.some_column])
result = model.Session.execute(query).fetchall() # or .fetchmany() or .fetchone() -
Types
- String(length=None) [better use Unicode instead]
- Integer
- SmallInteger
- Numeric(precision=10, length=2)
- Float(precision=10)
- DateTime [corresponds to datetime.datetime]
- Date [corresponds to datetime.date]
- Time [corresponds to datetime.time]
- Binary(length=None)
- Boolean
- Unicode(length=None)
- PickleType
Cookie-based Sessions
- Beaker home page
-
development.ini
- beaker.session.key = myproject_session (name of the session cookie being sent to the browser)
- beaker.session.secret = somesecret (random string that the cookie string sent to the user is signed with)
- beaker.session.cookie_expires = True (whether the cookie is a session cookie that expires when the browser is closed)
- beaker.session.timeout = ... (time in seconds until the session times out)
- beaker.session.data_dir = ... (path where the 'sessions' directory is located storing the session data)
- beaker.session.type = ... (Storage type for session information.)
- dbm stores sessions in files on disk
- file stores sessions in files on disk
- memory stores sessions in RAM
- ext:memcached stores sessions on memcached servers. Memcache servers are configured as beaker.session.url = server1, server2, ..
- The session variable comes from pylons.session and is imported in your lib/base.py. It behaves like a dictionary.
-
Loading a value from a session:
value = session['whatever']
-
Saving a value into the session:
session['whatever'] = value
session.save() -
Deleting a key from the session:
del session['whatever']
session.save() -
Clearing the session:
session.clear() session.save()
Formencode
-
Documentation
- Formencode web site
- Forms are validated through subclasses of formencode.schema
- Custom validators are usually subclasses of formencode.FancyValidator
- Writing custom validators
-
Using formencode:
- Define a validation schema
- Decorate your controller's method/action (@formencode.validate(MySchema))
- The validated parameter will be available in the self.form_result dictionary. Note: If the schema changes values (like if specifying if_empty) then this will not be visible in the request.params dictionary.
- If the form that the user submitted does not validate then formencode will re-run the current action (or run the action that you specify as form) as in @formencode.validate(MySchema, form='anotheraction') and display error messages right above the input fields with a CSS class of '.error-message'
-
Example validation class:
# My form schema
class MySchema(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
days = formencode.validators.IntRange(min=0, max=365, not_empty=True)
name = formencode.validators.MaxLength(50)# MySchema schema without the 'days' field
class MySchema2(MySchema):
days = None # (removes days field from the superclass)# MySchema schema with additional 'comment' field
class MySchema3(MySchema):
comment = String(min=20) -
Subclass variables:
- allow_extra_fields = False (whether to display an error if fields are found in the POST request that are not mentioned in this subclass)
- filter_extra_fields = False (whether to remove fields that are not mentioned in this subclass) are missing in the POST request but defined in this subclass)
- ignore_key_missing = False (whether to display an error if fields are missing in the POST request but defined in this subclass)
- if_key_missing (fields that are missing in the POST request will get this value assigned)
-
(Fancy)Validator variables:
- if_empty = False (default value in case of an empty field)
- not_empty = False (whether the field is allowed to be left empty)
- strip = False (whether the field will be stripped from whitespace)
- if_invalid = ... (return this value if the user entered something invalid)
- if_missing = ... (return this value if the field is missing from the POST request)
Javascript libraries
- jQuery (small, simple Javascript utility library with lots of plugins)
- Prototype (Javascript utility library)
- script.aculo.us (Javascript effects library built on top of Prototype)
Webhelpers
- The functions from webhelpers.rails are imported automatically
- Controllers and templates can access the webhelpers module by the 'h' (helper) name
- Links:
Internationalisation (i18n)
-
Install Babel:
- Debian/Ubuntu: aptitude install python-pybabel (formerly accidentally named "python-babel" but was renamed later)
- Other operatings systems: easy_install Babel
- Wiki article
- Edit your setup.py and enable the message_extractors section to parse gettext strings in templates.
-
Use the "_" function (an alias to the gettext function) everywhere you would use normal strings.
- Controllers: _('Hello World')
- Mako templates: ${ _('Hello World') }
-
First time:
- Create an english pot template file in the i18n directory: python setup.py extract_messages
- Create a po file for every language (here the language is 'es'): python setup.py init_catalog -l es
- Edit the po file in i18n/es/LC_MESSAGES/*.po and add translated strings in the msgstr fields
- Compile the po files into mo files: python setup.py compile_catalog
-
Update:
- Overwrite the english pot template:: python setup.py extract_messages
- Update the po files with new strings (does not overwrite the already translated strings): python setup.py update_catalog -l es
- Compile the po files into mo files: python setup.py compile_catalog
- Hint: paster serve --reload does not detect changes in i18n. You will have to restart the web server.
-
To set the gettext language matching the browser language add this to your lib/base.py:
def __before__(self):
user_agent_language = request.languages[0][0:2]
set_lang(user_agent_language)request.languages is an array of preferred languages that the users sets in the browser. I.e. ['de', 'en', 'en-us'].
Unit Tests (Nose)
Community
- IRC: #pylons @ irc.freenode.net
- Tags:
Recent comments
8 hours 8 min ago
2 days 16 hours ago
3 days 13 hours ago
6 days 3 hours ago
6 days 3 hours ago
6 days 9 hours ago
1 week 13 hours ago
1 week 3 days ago
1 week 3 days ago
1 week 4 days ago