Skip to main content

Generic user registration for the Pyramid web framework

Project description

pluserable provides generic user registration for the Pyramid web framework, if your web app uses SQLAlchemy.

It is a pluggable web application that provides user registration, login, logout and change password functionality. pluserable follows a policy of minimal interference, so your app can mostly keep its existing models.

The current release of pluserable has a security issue inherited from horus. Do not use it in production. The next release will fix this situation.

Minimal integration

  • Create a virtualenv and activate it. Install pyramid and create your pyramid project.

  • Edit your to add “pluserable” to the dependencies in the install_requires list.

  • Run python develop on your project to install all dependencies into your virtualenv.

  • Create your SQLAlchemy declarative initialization.

  • Create models inheriting from pluserable’ abstract models. Find an example in the file pluserable/tests/

    Then all you need to do is tell the class where to find your declarative base you and are good to go!

  • Include pluserable inside your main() function like this:

    # Tell pluserable which SQLAlchemy scoped session to use:
    from hem.interfaces import IDBSession
    registry = config.registry
    registry.registerUtility(my_sqlalchemy_scoped_session, IDBSession)

    With the above config.scan_pluserable() call, you need to edit your .ini configuration file and tell pluserable which model classes to use like this:

    pluserable.user_class = my_app.models:User
    pluserable.activation_class = my_app.models:Activation

    As an alternative to config.scan_pluserable() plus that configuration, you can register the classes explicitly if you so prefer. This must be done above config.include('pluserable'):

    # Tell pluserable which models to use:
    from pluserable.interfaces import IUserClass, IActivationClass
    registry.registerUtility(User, IUserClass)
    registry.registerUtility(Activation, IActivationClass)
  • Configure pluserable.login_redirect and pluserable.logout_redirect (in your .ini configuration file) to set the redirection routes.

  • If you haven’t done so yet, configure an HTTP session factory according to the Sessions chapter of the Pyramid documentation.

  • Create your database and tables. Maybe even an initial user.

  • Be sure to pass an authentication_policy argument in the config = Configurator(...) call. Refer to Pyramid docs for details.

  • By now the login form should appear at /login, but /register shouldn’t.

  • Include the package pyramid_mailer for the validation e-mail and “forgot password” e-mail:

  • The /register form should appear, though ugly. Now you have a choice regarding user activation by email:

    • You may just disable it by setting, in your .ini file:

      pluserable.require_activation = False
    • Otherwise, configure pyramid_mailer according to its documentation and test the registration page.

  • If you are using pyramid_tm or the ZopeTransactionManager, your minimal integration is done. (The pages are ugly, but working. Keep reading…)

Need to session.commit()?

pluserable does not require pyramid_tm or the ZopeTransactionManager with your session but if you do not use them you do have to take one extra step. We don’t commit transactions for you because that just wouldn’t be nice!

All you have to do is subscribe to the extension events and commit the session yourself. This also gives you the chance to do some extra processing:

from import (
    PasswordResetEvent, NewRegistrationEvent,
    RegistrationActivatedEvent, ProfileUpdatedEvent)

def handle_request(event):
    request = event.request
    session = request.registry.getUtility(IDBSession)

self.config.add_subscriber(handle_request, PasswordResetEvent)
self.config.add_subscriber(handle_request, NewRegistrationEvent)
self.config.add_subscriber(handle_request, RegistrationActivatedEvent)
self.config.add_subscriber(handle_request, ProfileUpdatedEvent)

Whether or not to have a “username” field

It is important that you analyze the characteristics of your web application and decide whether you need a username field for users to log in with. pluserable provides 2 modes of operation:

  • email + username: The user chooses a username when registering and later she can log in by providing either the username or the email address. Therefore, usernames may NOT contain the @ character. This mode is the default. It is expressed by the configuration setting pluserable.handle = usermail
  • email only: There is no username field and users only provide their email address. You enable this mode by:
    • Making your User model subclass NoUsernameMixin instead of UsernameMixin;
    • Adding this configuration setting: pluserable.handle = email, which will make pluserable default to schemas that contain email fields instead of username fields.

If you make this change and want to keep your data you must deal with the existing (or missing) “username” column yourself.

Changing the forms

If you would like to modify any of the forms, you just need to register the new deform class to be used.

The interfaces you have available to override from pluserable.interfaces are:

  • IPluserableLoginForm
  • IPluserableRegisterForm
  • IPluserableForgotPasswordForm
  • IPluserableResetPasswordForm
  • IPluserableProfileForm

This is how you would do it (MyForm being a custom deform Form class):

config.registry.registerUtility(MyForm, IPluserableLoginForm)

Changing the templates

If you would like to substitute the templates you can use pyramid’s override_asset:


The templates you have available to override are:

  • login.mako
  • register.mako
  • forgot_password.mako
  • reset_password.mako
  • profile.mako

If you would like to override the templates with Jinja2, or any other templating language, just override the view configuration:

config.add_view('pluserable.views.AuthController', attr='login',
    route_name='login', renderer='yourapp:templates/login.jinja2')
    attr='forgot_password', route_name='forgot_password',
    attr='reset_password', route_name='reset_password',
config.add_view('pluserable.views.RegisterController', attr='register',
    route_name='register', renderer='yourapp:templates/register.jinja2')
config.add_view('pluserable.views.ProfileController', attr='profile',
    route_name='profile', renderer='yourapp:templates/profile.jinja2')

Changing strings

Take a look at this class. This is where we store all the strings in pluserable. If you’d like to change one or two messages, simply subclass this, then do:

from pluserable.interfaces import IUIStrings
config.registry.registerUtility(MyStringsClass, IUIStrings)

Changing the primary key column name

If you wish to override the primary key attribute name, you can do so by creating a new mixin class:

class NullPkMixin(Base):
    abstract = True
    _idAttribute = 'pk'

    def pk(self):

    def id(self):
        return None

class User(NullPkMixin, UserMixin):

pluserable development


If you would like to help make any changes to pluserable, you can run its unit tests with py.test:


To check test coverage:

py.test --cov-report term-missing --cov pluserable

The tests can also be run in parallel:

py.test -n4

We are going to use this build server:!/nandoflorestan/pluserable

Origin of the project

pluserable is a fork of horus, a project started by John Anderson:

The differences are:

  • pluserable lets you log in with an email (or a username); horus only lets you log in with a username.
  • pluserable does not have horus’ admin views – they were rarely used.
  • pluserable allows you to pick a subset of the views for your project; horus always registers all of the routes and views.
  • pluserable does not include an outdated version of bootstrap.
  • pluserable does not have a scaffolding script.
  • pluserable uses pyramid.compat rather than the six library.
  • pluserable uses the bag library for a maintained version of FlashMessage.

Project details

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for pluserable, version 0.1.0
Filename, size File type Python version Upload date Hashes
Filename, size pluserable-0.1.0.tar.gz (28.7 kB) File type Source Python version None Upload date Hashes View

Supported by

AWS AWS Cloud computing Datadog Datadog Monitoring DigiCert DigiCert EV certificate Facebook / Instagram Facebook / Instagram PSF Sponsor Fastly Fastly CDN Google Google Object Storage and Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Salesforce Salesforce PSF Sponsor Sentry Sentry Error logging StatusPage StatusPage Status page