Blog |

Using a Request Factory in Pyramid to write a little less code

Using a Request Factory in Pyramid to write a little less code
Table of Contents

At Rollbar.com, we’ve been using Pyramid as our web framework
and have been pretty happy with it. It’s lightweight and mostly stays out of our way.

Pyramid doesn’t have a global request object that you can just import[1], so it makes you pass
around request wherever you need it. That results in a lot of library code that looks like this:

# lib/helpers.py
def flash_success(request, body, title=''):
    request.session.flash({'body': body, 'title': title'})

and a lot of view code that looks like this:

# views/auth.py
@view_config(route_name='auth/login')
def login(request):
    # (do the login...)
    helpers.flash_success(request, "You're now logged in.")
    # (redirect...)

That is, there ends up being a lot of function calls that pass request as their first argument.
Wouldn’t it be nicer if we could attach these functions as methods on request itself? That would
save a few characters every time we call them, and let us stop thinking about whether request is
the first or last argument. Pyramid facilitates this by letting us provide our own
Request Factory:

from pyramid.request import Request

class MyRequest(Request):
    def hello(self):
        print "hello!"

def main(global_config, **settings):
    config = Configurator(settings=settings, request_factory=MyRequest)
    # ...

Now the request passed to our view methods, and everywhere else in our app, has our hello method.

So, what can we do with this that’s actually useful? In our codebase, we have a few convenience
methods to get data about the logged-in user, flash messages, and check if features are enabled.

Here it is, unedited, in its entirety:

class MoxRequest(pyramid.request.Request):
    # logged-in-user access
    @util.CachedAttribute
    def user_id(self):
        from pyramid.security import authenticated_userid
        user_id = authenticated_userid(self)
        log.debug('authenticated user id: %r', user_id)
        return user_id

    @util.CachedAttribute
    def user(self):
        user_id = self.user_id
        if user_id:
            return model.User.get(user_id)
        return None

    @util.CachedAttribute
    def username(self):
        if self.user:
            return self.user.username
        else:
            return None

    def gater_check(self, feature_name):
        return self.registry.settings.get('gater.%s' % feature_name) == 'on'

    # flash methods
    def flash_success(self, body, title=''):
        self._flash_message(body, title=title, queue='success')

    def flash_info(self, body, title=''):
        self._flash_message(body, title=title, queue='info')

    def flash_warning(self, body, title=''):
        self._flash_message(body, title=title, queue='warning')

    def flash_error(self, body, title=''):
        self._flash_message(body, title=title, queue='error')

    def _flash_message(self, body, title='', queue=''):
        self.session.flash({'title': title, 'body': body}, queue=queue)

This just sits in our top-level init.py, along with the main() entry point.

Notes: code>@util.CachedAttribute contains
this recipe.
“Mox” is an easy-to-type codename, named after
these mountains.

[1] I’m still not sold on this, but I’m getting by. It arguably causes problems with testing and
such, but it is pretty nice to magically from flask import request.

"Rollbar allows us to go from alerting to impact analysis and resolution in a matter of minutes. Without it we would be flying blind."

Error Monitoring

Start continuously improving your code today.

Get Started Shape