bukzor
bukzor

Reputation: 38532

pyramid: deduplicate similar routes

I have a problem with repeated code in the routes of our pyramid app. I'm pretty sure I'm doing it wrong, but don't know how to do better. Our app essentially has three "modes" which are represented as prefixes to the URL path. With no prefix, we're in "prod" mode, then we have "/mock" and "/old" prefixes which use the same views with different backends to fetch the data.

The code turns out looking like this:

def routes(config):
    """Add routes to the configuration."""
    config.add_route('my_view:old', '/old/my_view')
    config.add_route('my_view:prod', '/my_view')
    config.add_route('my_view:mock', '/mock/my_view')

@view_config(route_name='my_view:mock', renderer='string')
def my_view_mock(request):
    return my_view(request, data.mock)


@view_config(route_name='my_view:prod', renderer='string')
def my_view_prod(request):
    return my_view(request, data.prod)


@view_config(route_name='my_view:old', renderer='string')
def my_view_old(request):
    return my_view(request, data.old)


def my_view(request, data):
    results = data.query(**request.json)

What's worse, is this pattern is repeated for all of our endpoints, resulting in a ton of nearly-duplicate boilerplate code.

How can I teach pyramid about my setup in some centralized manner and get rid of this boilerplate?

Upvotes: 2

Views: 97

Answers (1)

Michael Merickel
Michael Merickel

Reputation: 23341

Well here's an option. It requires you to define a unique object for each view. The nice part is that you can define that object and then each route can create it differently ... imagine factory=lambda request: MyView(request, old=True) instead of using the exact same MyView(request) object for each route.

def routes(config):
    """Add routes to the configuration."""
    config.add_directive('add_routed_resource', add_routed_resource)

    config.add_routed_resource('my_view', MyView)

def add_routed_resource(config, name, factory):
    routes = [
        ('%s:old', '/old/%s-errors', lambda request: factory(request, old=True)),
        ('%s:prod', '/%s', factory),
        ('%s:mock', '/mock/%s', lambda request: factory(request, mock=True)),
    ]
    for name_fmt, pattern_fmt in routes:
        config.add_route(
            name_fmt % name,
            pattern_fmt % name,
            factory=factory,
            use_global_views=True,
        )

class MyView:
    def __init__(self, request, old=False, mock=False):
        self.request = request
        self.old = old
        self.mock = mock

    @reify
    def data(self):
        # let's assume sqlalchemy query to load the data?
        q = self.request.db.query(...)
        if self.old:
            q = q.filter_by(old=True)
        return q.one()

@view_config(context=MyView, renderer='json')
def my_view(context, request):
    return context.data

Upvotes: 1

Related Questions