Basic Layout

The starter files generated by the pyramid_routesalchemy scaffold are basic, but they provide a good orientation for the high-level patterns common to most url dispatch -based Pyramid projects.

The source code for this tutorial stage can be browsed at http://github.com/Pylons/pyramid/tree/1.2-branch/docs/tutorials/wiki2/src/basiclayout/.

App Startup with __init__.py

A directory on disk can be turned into a Python package by containing an __init__.py file. Even if empty, this marks a directory as a Python package. We use __init__.py both as a package marker and to contain configuration code.

The generated development.ini file is read by paster which looks for the application module in the use variable of the app:main section. The entry point is defined in the Setuptools configuration of this module, specifically in the setup.py file. For this tutorial, the entry point is defined as tutorial:main and points to a function named main.

First we need some imports to support later code:

1
2
3
4
5
from pyramid.config import Configurator
from sqlalchemy import engine_from_config

from tutorial.models import initialize_sql

Next we define the main function and create a SQLAlchemy database engine from the sqlalchemy. prefixed settings in the development.ini file’s [app:main] section. This will be a URI (something like sqlite://):

1
2
3
4
def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    engine = engine_from_config(settings, 'sqlalchemy.')

We then initialize our SQL database using SQLAlchemy, passing it the engine:

    initialize_sql(engine)

The next step is to construct a Configurator:

    config = Configurator(settings=settings)

settings is passed to the Configurator as a keyword argument with the dictionary values passed by PasteDeploy as the **settings argument. This will be a dictionary of settings parsed from the .ini file, which contains deployment-related values such as pyramid.reload_templates, db_string, etc.

We now can call pyramid.config.Configurator.add_static_view() with the arguments static (the name), and tutorial:static (the path):

    config.add_static_view('static', 'tutorial:static', cache_max_age=3600)

This registers a static resource view which will match any URL that starts with /static/. This will serve up static resources for us from within the static directory of our tutorial package, in this case, via http://localhost:6543/static/ and below. With this declaration, we’re saying that any URL that starts with /static should go to the static view; any remainder of its path (e.g. the /foo in /static/foo) will be used to compose a path to a static file resource, such as a CSS file.

Using the configurator we can also register a route configuration via the pyramid.config.Configurator.add_route() method that will be used when the URL is /:

    config.add_route('home', '/')

Since this route has a pattern equalling / it is the route that will be matched when the URL / is visted, e.g. http://localhost:6543/.

Mapping the home route to code is done by registering a view. You will use pyramid.config.Configurator.add_view() in URL dispatch to register views for the routes, mapping your patterns to code:

    config.add_view('tutorial.views.my_view', route_name='home',
                    renderer='templates/mytemplate.pt')

The first positional add_view argument tutorial.views.my_view is the dotted name to a function we write (generated by the pyramid_routesalchemy scaffold) that is given a request object and which returns a response or a dictionary. This view also names a renderer, which is a template which lives in the templates subdirectory of the package. When the tutorial.views.my_view view returns a dictionary, a renderer will use this template to create a response.

Finally, we use the pyramid.config.Configurator.make_wsgi_app() method to return a WSGI application:

    return config.make_wsgi_app()

Our final __init__.py file will look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from pyramid.config import Configurator
from sqlalchemy import engine_from_config

from tutorial.models import initialize_sql

def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    engine = engine_from_config(settings, 'sqlalchemy.')
    initialize_sql(engine)
    config = Configurator(settings=settings)
    config.add_static_view('static', 'tutorial:static', cache_max_age=3600)
    config.add_route('home', '/')
    config.add_view('tutorial.views.my_view', route_name='home',
                    renderer='templates/mytemplate.pt')
    return config.make_wsgi_app()


Content Models with models.py

In a SQLAlchemy-based application, a model object is an object composed by querying the SQL database which backs an application. SQLAlchemy is an “object relational mapper” (an ORM). The models.py file is where the pyramid_routesalchemy scaffold put the classes that implement our models.

Let’s take a look. First, we need some imports to support later code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import transaction

from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import Unicode

from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base

from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker

from zope.sqlalchemy import ZopeTransactionExtension

Next we set up a SQLAlchemy “DBSession” object:

1
2
DBSession = scoped_session(sessionmaker(
                             extension=ZopeTransactionExtension()))

We also need to create a declarative Base object to use as a base class for our model:

Base = declarative_base()

To give a simple example of a model class, we define one named MyModel:

1
2
3
4
5
6
7
8
9
class MyModel(Base):
    __tablename__ = 'models'
    id = Column(Integer, primary_key=True)
    name = Column(Unicode(255), unique=True)
    value = Column(Integer)

    def __init__(self, name, value):
        self.name = name
        self.value = value

Our sample model has an __init__ that takes a two arguments (name, and value). It stores these values as self.name and self.value within the __init__ function itself. The MyModel class also has a __tablename__ attribute. This informs SQLAlchemy which table to use to store the data representing instances of this class.

Next we define a function named populate which adds a single model instance into our SQL storage and commits a transaction:

1
2
3
4
5
6
def populate():
    session = DBSession()
    model = MyModel(name=u'root',value=55)
    session.add(model)
    session.flush()
    transaction.commit()

The function doesn’t do a lot in this case, but it’s there to illustrate how an application requiring many objects to be set up could work.

Lastly we have a function named initialize_sql which receives a SQL database engine and binds it to our SQLAlchemy DBSession object. It also calls the populate function, to do initial database population. This is the initialization function that is called from __init__.py above.

1
2
3
4
5
6
7
8
def initialize_sql(engine):
    DBSession.configure(bind=engine)
    Base.metadata.bind = engine
    Base.metadata.create_all(engine)
    try:
        populate()
    except IntegrityError:
        transaction.abort()

Here is the complete source for models.py:

 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
import transaction

from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import Unicode

from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base

from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker

from zope.sqlalchemy import ZopeTransactionExtension

DBSession = scoped_session(sessionmaker(
                             extension=ZopeTransactionExtension()))
Base = declarative_base()

class MyModel(Base):
    __tablename__ = 'models'
    id = Column(Integer, primary_key=True)
    name = Column(Unicode(255), unique=True)
    value = Column(Integer)

    def __init__(self, name, value):
        self.name = name
        self.value = value

def populate():
    session = DBSession()
    model = MyModel(name=u'root',value=55)
    session.add(model)
    session.flush()
    transaction.commit()
    
def initialize_sql(engine):
    DBSession.configure(bind=engine)
    Base.metadata.bind = engine
    Base.metadata.create_all(engine)
    try:
        populate()
    except IntegrityError:
        transaction.abort()