Welcome to pyramid_formalchemy’s documentation!

About

pyramid_formalchemy provides a CRUD interface for pyramid based on FormAlchemy

It also allows to use FormAlchemy to render forms in your application.

Installation

pyramid_formalchemy must be a dependency of you application, so you must add “pyramid_formalchemy” as a dependency in your setup.py under install_requires. You must now update your environment, in case of buildout, by running a new buildout. If you want nicer jquery functionality also add a dependency for fa.jquery.

pyramid_formalchemy also provides a pyramid scaffold. It can be used to add a skeleton to an existing project or to create a new project. If you create a new project, you must first install pyramid_formalchemy in your python environment, either with pip:

$ pip install pyramid_formalchemy pyramid_fanstatic fa.jquery

or with easy_install:

$ easy_install pyramid_formalchemy pyramid_fanstatic fa.jquery

Only after that, the pyramid scaffold becomes available. The template was made with the idea that it can be used to extend existing applications. It does not create an app for you. For bootstrapping your app, you need another pyramid scaffold. The provided template works well with alchemy, Akhet. To bootstrap an application, call paster like that:

$ pcreate -s alchemy -s pyramid_fa myapp

Or for Akhet:

$ pcreate -s akhet -s pyramid_fa myapp

The application is created by akhet, akhet does not know about pyramid_formalchemy, and pyramid_formalchemy cannot modify the app configuration. So you have to do this by hand. First, you must add the install dependency like explained earlier. Second, you must add the following line in the main method that returns the wsgi app, directly after Configurator has been created (The example assumes that the Configurator instance is stored under the name “config”):

...
config.include('myapp.fainit')
...

More details are explained in myapp/README_FORMALCHEMY.txt. The process is the same for other templates. For the pyramid ones there are additional changes necessary that are also explained in fareadme.txt

To add the minimum configuration to an existing application, you should be able to run:

$ pcreate -s pyramid_fa myapp

All files that paster creates are prefixed with fa, and should not interfere with existing code. The other customizations described for fresh applications are also valid for existing apps.

Basic usage

Add an empty forms.py module at the root of your project.

Now you just need to include two lines of configuration in the __init__.main() function of your project. Here is the one used for testing:

from pyramid.config import Configurator
from sqlalchemy import engine_from_config

from pyramidapp.models import initialize_sql
from pyramidapp import events; events #pyflakes

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', 'pyramidapp:static')
    config.add_route('home', '/')
    config.add_view('pyramidapp.views.my_view',
                    route_name='home',
                    renderer='templates/mytemplate.pt')

    # pyramid_formalchemy's configuration
    config.include('pyramid_formalchemy')

    # register an admin UI
    config.formalchemy_admin('admin', package='pyramidapp')

    # register an admin UI for a single model
    config.formalchemy_model('foo', package='pyramidapp', model='pyramidapp.models.Foo')

    # register custom model listing
    config.formalchemy_model_view('admin',
                                  model='pyramidapp.models.Foo',
                                  context='pyramid_formalchemy.resources.ModelListing',
                                  renderer='templates/foolisting.pt',
                                  attr='listing',
                                  request_method='GET',
                                  permission='view')

    # register custom model view
    config.formalchemy_model_view('admin',
                                  model='pyramidapp.models.Foo',
                                  context='pyramid_formalchemy.resources.Model',
                                  name='',
                                  renderer='templates/fooshow.pt',
                                  attr='show',
                                  request_method='GET',
                                  permission='view')

    return config.make_wsgi_app()

This will render the basic formalchemy interface.

It’s better to enable the jquery features like this:

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', 'pyramidapp:static')
    config.add_route('home', '/')
    config.add_view('pyramidapp.views.my_view',
                    route_name='home',
                    renderer='templates/mytemplate.pt')

    # pyramid_formalchemy's configuration
    config.include('pyramid_formalchemy')
    config.include('fa.jquery')

    # register an admin UI
    config.formalchemy_admin('/admin', package='pyramidapp', view='fa.jquery.pyramid.ModelView')

    # register an admin UI for a single model
    config.formalchemy_model('/foo', package='pyramidapp',
                                    view='fa.jquery.pyramid.ModelView',
                                    model='pyramidapp.models.Foo')

    return config.make_wsgi_app()

That’s it. Now launch your app, browse /admin/ and enjoy it !

About Models

Here is a typical model to use full features of pyramid_formalchemy. Notice that the fields are defined using formalchemy.Column():

class User(Base):
    __label__ = _('User')       # label used in UI
    __plural__ = _('Users')     # plural used in UI
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    name = Column(Unicode, nullable=False, label=_('Name')) # label is used in forms

    # the renderer argument will be used for the group relation. You can use a
    # backref_renderer option to set the renderer of the users relation
    group_id = Column(Integer, ForeignKey('groups.id'),
                      renderer=renderers.autocomplete_relation(filter_by='name'))

    group = relation("Group", uselist=False, backref='users')

    def __unicode__(self):
        return self.name

See also pyramid_formalchemy.actions to customize form buttons per model.

Advanced usage

Custom view, factory, forms

In the toward examples we just pass a package parameter to configure(). By default the function will try to load package.models, package.models.DBSession and package.forms but you can override this. In this case you don’t need to specify a package:

config.formalchemy_admin(config,
                         forms='formalchemy_project.forms',
                         models='formalchemy_project.mymodels',
                         session_factory='formalchemy_project.session.Session',
                         )

You can also change the path prefix used. pyramid_formalchemy will use /admin/ by default. See configure().

Having fun with the query_factory parameter

def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    engine = engine_from_config(settings, 'sqlalchemy.')
    models.initialize_sql(engine)
    config = Configurator(settings=settings)
    config.include('pyramid_tm')
    config.add_translation_dirs('formalchemy_project:locale/')

    # pyramid_formalchemy's configuration
    config.include('pyramid_formalchemy')
    config.include('fa.jquery')
    config.include('fa.extjs')

    # Admin UI (Used for the demo. Not really useful here...)
    config.formalchemy_admin('/extjs', package='formalchemy_project', view='fa.extjs.ModelView')
    config.formalchemy_admin('/admin', package='formalchemy_project', view='fa.jquery.pyramid.ModelView')

    # Article admin UI. Use a custom query_factory to filter by user

    # Here is the interesting part
    def query_factory(request, query, id=None):
        """this query factory use request.matchdict to retrieve user's
        articles. Of course, you can do anything like check that the user found
        in matchdict is the REMOTE_USER"""
        user = request.session_factory.query(models.User).filter_by(name=request.matchdict['user']).one()
        if id:
            return query.filter_by(user=user, id=id).one()
        else:
            return query.filter_by(user=user)

    config.formalchemy_model('/articles/{user}', package='formalchemy_project',
                             model='formalchemy_project.models.Article',
                             query_factory=query_factory,
                             view='fa.jquery.pyramid.ModelView')

    return config.make_wsgi_app()

Custom views per model

You can also register custom CRUD views per ModelListing:

config.formalchemy_model_view('admin',
                              model='pyramidapp.models.Foo',
                              context='pyramid_formalchemy.resources.ModelListing',
                              renderer='templates/foolisting.pt',
                              attr='listing',
                              request_method='GET',
                              permission='view')

and per Model:

config.formalchemy_model_view('admin',
                              model='pyramidapp.models.Foo',
                              context='pyramid_formalchemy.resources.Model',
                              name='',
                              renderer='templates/fooshow.pt',
                              attr='show',
                              request_method='GET',
                              permission='view')

Events

pyramid_formalchemy got some hooks to help you to customize things. See pyramid_formalchemy.events.

Actions

pyramid_formalchemy allow you to customize links and actions of your views. See pyramid_formalchemy.actions.

Setting permissions

pyramid_formalchemy takes care of some __acl__ attributes.

Setting permissions for the global interface

You just need to subclass the default factory in your application:

from pyramid.config import Configurator
from sqlalchemy import engine_from_config
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.authentication import RemoteUserAuthenticationPolicy
from pyramid_formalchemy.resources import Models
from pyramid.security import Allow, Authenticated, ALL_PERMISSIONS

from pyramidapp.models import initialize_sql

class ModelsWithACL(Models):
    """A factory to override the default security setting"""
    __acl__ = [
            (Allow, 'admin', ALL_PERMISSIONS),
            (Allow, Authenticated, 'view'),
            (Allow, 'editor', 'edit'),
            (Allow, 'manager', ('new', 'edit', 'delete')),
        ]

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

    # configure the security stuff
    config = Configurator(settings=settings,
                          authentication_policy=RemoteUserAuthenticationPolicy(),
                          authorization_policy=ACLAuthorizationPolicy())

    config.add_static_view('static', 'pyramidapp:static')
    config.add_route('home', '/')
    config.add_view('pyramidapp.views.my_view',
                    route_name='home',
                    renderer='templates/mytemplate.pt')

    # pyramid_formalchemy's configuration
    config.include('pyramid_formalchemy')
    config.formalchemy_admin('admin', package='pyramidapp',
                             factory=ModelsWithACL) # use the secure factory

    return config.make_wsgi_app()

Setting permissions per model

You can also add an __acl__ attribute to your model class:

class Bar(Base):
    __tablename__ = 'bar'
    __acl__ = [
            (Allow, 'admin', ALL_PERMISSIONS),
            (Allow, 'bar_manager', ('view', 'new', 'edit')),
        ]
    id = Column(Integer, primary_key=True)
    foo = Column(Unicode(255))

Indices and tables