Using a View Mapper to Pass Query Parameters as Keyword Arguments¶
Pyramid supports a concept of a "view mapper". See Using a View Mapper for general information about view mappers. You can use a view mapper to support an alternate convenience calling convention in which you allow view callables to name extra required and optional arguments which are taken from the request.params dictionary. So, for example, instead of:
1@view_config()
2def aview(request):
3 name = request.params['name']
4 value = request.params.get('value', 'default')
5 ...
With a special view mapper you can define this as:
@view_config(mapper=MapplyViewMapper)
def aview(request, name, value='default'):
...
The below code implements the MapplyViewMapper
. It works as a mapper for
function view callables and method view callables:
1import inspect
2import sys
3
4from pyramid.view import view_config
5from pyramid.response import Response
6from pyramid.config import Configurator
7from waitress import serve
8
9PY3 = sys.version_info[0] == 3
10
11if PY3:
12 im_func = '__func__'
13 func_defaults = '__defaults__'
14 func_code = '__code__'
15else:
16 im_func = 'im_func'
17 func_defaults = 'func_defaults'
18 func_code = 'func_code'
19
20def mapply(ob, positional, keyword):
21
22 f = ob
23 im = False
24
25 if hasattr(f, im_func):
26 im = True
27
28 if im:
29 f = getattr(f, im_func)
30 c = getattr(f, func_code)
31 defaults = getattr(f, func_defaults)
32 names = c.co_varnames[1:c.co_argcount]
33 else:
34 defaults = getattr(f, func_defaults)
35 c = getattr(f, func_code)
36 names = c.co_varnames[:c.co_argcount]
37
38 nargs = len(names)
39 args = []
40 if positional:
41 positional = list(positional)
42 if len(positional) > nargs:
43 raise TypeError('too many arguments')
44 args = positional
45
46 get = keyword.get
47 nrequired = len(names) - (len(defaults or ()))
48 for index in range(len(args), len(names)):
49 name = names[index]
50 v = get(name, args)
51 if v is args:
52 if index < nrequired:
53 raise TypeError('argument %s was omitted' % name)
54 else:
55 v = defaults[index-nrequired]
56 args.append(v)
57
58 args = tuple(args)
59 return ob(*args)
60
61
62class MapplyViewMapper(object):
63 def __init__(self, **kw):
64 self.attr = kw.get('attr')
65
66 def __call__(self, view):
67 def wrapper(context, request):
68 keywords = dict(request.params.items())
69 if inspect.isclass(view):
70 inst = view(request)
71 meth = getattr(inst, self.attr)
72 response = mapply(meth, (), keywords)
73 else:
74 # it's a function
75 response = mapply(view, (request,), keywords)
76 return response
77
78 return wrapper
79
80@view_config(name='function', mapper=MapplyViewMapper)
81def view_function(request, one, two=False):
82 return Response('one: %s, two: %s' % (one, two))
83
84class ViewClass(object):
85 __view_mapper__ = MapplyViewMapper
86 def __init__(self, request):
87 self.request = request
88
89 @view_config(name='method')
90 def view_method(self, one, two=False):
91 return Response('one: %s, two: %s' % (one, two))
92
93if __name__ == '__main__':
94 config = Configurator()
95 config.scan('.')
96 app = config.make_wsgi_app()
97 serve(app)
98
99# http://localhost:8080/function --> (exception; no "one" arg supplied)
100
101# http://localhost:8080/function?one=1 --> one: '1', two: False
102
103# http://localhost:8080/function?one=1&two=2 --> one: '1', two: '2'
104
105# http://localhost:8080/method --> (exception; no "one" arg supplied)
106
107# http://localhost:8080/method?one=1 --> one: '1', two: False
108
109# http://localhost:8080/method?one=1&two=2 --> one: '1', two: '2'