Dashboard > Pylons Miscellaneous > Home > Pylons Cheatsheet
  Pylons Miscellaneous Log In | Sign Up   View a printable version of the current page.  
  Pylons Cheatsheet
Added by Christoph Haas, last edited by Christoph Haas on Aug 02, 2008  (view change)
Labels: 
(None)

Note

The source of the document is available from a Mercurial repository at http://workaround.org/cgi-bin/hg-pylons-cheatsheet - please send in patches to email@christoph-haas.de although you may be reading the document on wiki.pylonshq.com. Thanks.

Pylons

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

  • Defining tables and ORM-mapping in model/__init__.py

    • Simple mapping:

      import sqlalchemy as sql
      import sqlalchemy.orm as orm
      
      # 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.name
      
      orm.mapper(MyModel, my_table)
      
  • 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:

    • General 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

  • 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

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'].

Community

  • IRC: #pylons @ irc.freenode.net

Related add-ons

  • FormAlchemy: Automatically creates HTML forms for SQLAlchemy schemas (mapped classes)
  • Paginator: Helps split up large numbers of results into pages and lets the user navigate through the pages
  • DBSprockets: Automatically creates Toscawidget forms and Formencode Validators for SQLAlchemy schemas (mapped classes)

...

Site running on a free Atlassian Confluence Open Source Project License granted to Pylons. Evaluate Confluence today.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.3.3 Build:#645 Feb 13, 2007) - Bug/feature request - Contact Administrators