Create dynamic class-views in pyramid

603 views Asked by At

I am trying to reduce my code writing in my Pyramid views and I'm trying to do it via a factory function.

Thus, instead of having something like this in my views.py:

class MyView(object):
    def __init__(self, request):
        self.request = request

    @view_config(route_name='view', renderer='templates/view.pt')
    def get(self):
        return dict(msg='Hello!')

I try having something like this instead:

def factory(cls_name, rtn, rndr, myfun):
    class Cls(object):
        def __init__(self, request):
            self.request = request

        @myfun(route_name=rtn, renderer=rndr)
        def get(self):
            return dict(msg='Hello!')

    Cls.__name__ = cls_name
    return Cls

MyView = factory('MyView', 'view', 'templates/view.pt', view_config)

The reason is, obviously, that I'll end up having many classes that could utilise the same class functionality and I want to reduce my code writing. In case you ask me to use inheritance instead, then I'm stuck as to how to configure my derived classes to have a parameterised decorator (like myfun). In essence, I need something like a template in C++.

So, even though the above second snippets does not generate any errors, and to me it seems to work just fine, when I try to run it in my test site (by replacing the second snippet with the second one), the routes are not understood by pyramid, so I'm getting a 404 error. Based on the error, I presume that my problem is probably related with how pyramid parses the views.py file to find view_config decorators, but I am not sure how to address it.

My __init__.py's main function is somehow like this:

def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """      
    config = Configurator(settings=settings)
    config.add_route('view', '/')
    config.scan()
    return config.make_wsgi_app()

Any help would be greatly appreciated, so thanks all in advance!

1

There are 1 answers

1
Sergey On BEST ANSWER

The most likely cause of you code not working is the details of the scan process performed by Pyramid on startup to find and process all @view_config decorators. For one thing, the @view_config docstring says:

    ``view_config`` will work ONLY on module top level members
    because of the limitation of ``venusian.Scanner.scan``.

So I guess either your classes are created too late in the startup process or something in your dynamically-generated classes confuses venusian. If you have a look at venusian's source code you may be able to find the cause.

However, I'd like to point out that the approach strikes me as a bit recursive... There are ways in Pyramid to register a view without any decorators. Then Pyramid authors added a layer of "syntax sugar" on top of that to simplify the most common use-case and be able to register a view by simply adding a decorator. Then you start fighting those decorators to be able to register a view by what looks like a function call :)

Have a look at pyramid.config.Configurator.add_view() - it already looks very much like your "factory function" but you may be able to write a simple wrapper around it if you want.

class MyView(object):
    def __init__(self, request):
        self.request = request

    def get(self):
        return dict(msg='Hello!')

config.add_view(MyView, attr='get', route_name='view_cats', renderer='templates/view_cats.pt')
config.add_view(MyView, attr='get', route_name='view_dogs', renderer='templates/view_dogs.pt')