Slide 1

Slide 1 text

Postcard from the Pyramid view Overview on View Lookup features

Slide 2

Slide 2 text

Hello, Bonjour Rach Belaid Vim Enthousiast, Python Developer, Postgres Fan, Git Lover, Belgian Erlang Hobbyist, rach @rachbelaid rachbelaid.com [email protected]

Slide 3

Slide 3 text

What are Views? Pyramid views are callable objects and can be : Function Method Class

Slide 4

Slide 4 text

Adding Views There is 2 ways to add views : - Via config method: Configurator.add_view(...) - Via decorator @view_config(...)

Slide 5

Slide 5 text

Example - add_view() from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response def hello_world(request): return Response('Hello world!') if __name__ == '__main__': config = Configurator() config.add_route('home', '/') config.add_view(hello_world, route_name='home') app = config.make_wsgi_app() server = make_server('0.0.0.0', 8080, app) server.serve_forever()

Slide 6

Slide 6 text

Example - view_config() from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response from pyramid.view import view_config @view_config(route_name='home') def hello_world(request): return Response('Hello world!') if __name__ == '__main__': config = Configurator() config.add_route('home', '/') config.scan() #run the app app = config.make_wsgi_app() server = make_server('0.0.0.0', 8080, app) server.serve_forever()

Slide 7

Slide 7 text

View ≠ Route - Routes match urls - Views match routes

Slide 8

Slide 8 text

From Request to View home profile / /username welcome welcome home welcome profile Requests Routes Views GET POST USER? GET GET Route Matching route matching View Lookup

Slide 9

Slide 9 text

View lookup ? After any routes matches “the view lookup subsystem takes over to find the most reasonable view callable for the matched route” WTF? Pyramid build a lookup resolution tree and detects conflict in the views configs. Use pviews cmd to analyse the views lookup

Slide 10

Slide 10 text

Example - Conflict detection ... def hello_world(request): return Response('Hello world!') def hi_world(request): return Response('Hello world!') if __name__ == '__main__': config = Configurator() config.add_route('home', '/') config.add_view(hello_world, route_name='home') config.add_view(hi_world, route_name='home') ... FAIL! pyramid.exceptions.ConfigurationConflictError

Slide 11

Slide 11 text

View lookup configs Pyramid allow you to configure a view to be matched using many parameters - route name - request method : POST, GET, ... - xhr - request params - match params - custom predicates function

Slide 12

Slide 12 text

Example - View Lookup route name ... def hello_world(request): return Response('Hello world!') def hi_world(request): return Response('Hello world!') if __name__ == '__main__': config = Configurator() config.add_route('test1', '/test1') config.add_route('test2', '/test2') config.add_view(hello_world, route_name='test1') config.add_view(hi_world, route_name='test2') ...

Slide 13

Slide 13 text

Example - View Lookup request method ... @view_config(route_name='test', request_method='GET') def hello_world(request): return Response('Hello world!') @view_config(route_name='test', request_method='POST') def thanks_you(request): return Response('Thanks you') if __name__ == '__main__': config = Configurator() config.add_route('test', '/test') config.scan() ...

Slide 14

Slide 14 text

Example - View Lookup Xhr ... @view_config(route_name='test') def hello_world(request): return Response('Hello world!') @view_config(route_name='test', xhr=True) def hello_world_xhr(request): val = json.dumps({'response':'Hello World'}) return Response(val) if __name__ == '__main__': config = Configurator() config.add_route('test', '/test') config.scan() ...

Slide 15

Slide 15 text

Example - View Lookup request param ... @view_config(route_name='welcome') def welcome_view(request): return Response('Hello') @view_config(route_name='welcome', request_param='submit=signup') def signup_view(request): return Response('Signup') @view_config(route_name='welcome', request_param='submit=signin') def signin_view(request): return Response('Signin') if __name__ == '__main__': config = Configurator() config.add_route('welcome', '/') config.scan() ...

Slide 16

Slide 16 text

Example - View Lookup match param ... @view_config(route_name='action', match_param='action=hello') def hello_view(request): return Response('Hello') @view_config(route_name='action', match_param='action=bye') def bye_view(request): return Response('Bye Bye') @view_config(route_name='action', match_param='action') def bye_view(request): return Response('Let’s continue') if __name__ == '__main__': config = Configurator() config.add_route('action', '/{action:\w+}') config.scan() ...

Slide 17

Slide 17 text

Example - View Lookup custom predicate ... def useless_predicate(info, request): from datetime import date today = date.today() return today.day == 29 && today.month == 2 @view_config(route_name='home', custom_predicate=useless_predicate) def useless_view(request): return Response('Leap year') if __name__ == '__main__': config = Configurator() config.add_route('home', '/') config.scan() ...

Slide 18

Slide 18 text

Others - Headers - Accept - Request Type - Context - check csrf

Slide 19

Slide 19 text

Pyramid Views: Function, Method, Class @view_config(route_name='test') def test(request): return Response('ok') Function class Test(object): def __init__(self, request): self.request = request @view_config(route_name='test') def test(self): return Response('ok') Method @view_config(route_name='test') class Test(object): def __init__(self, request): self.request = request def __call__(self): return Response('ok') Class

Slide 20

Slide 20 text

Class base views - example from ... class RESTView(object): def __init__(self, request): self.request = request @view_config(route_name='rest', request_method='GET') def get(self): return Response('get') @view_config(route_name='rest', request_method='POST') def post(self): return Response('post') @view_config(route_name='rest', request_method='DELETE') def delete(self): return Response('delete')

Slide 21

Slide 21 text

View defaults pyramid.view.view_defaults class decorator to provide defaults to the view configuration information

Slide 22

Slide 22 text

Example - view default ... @view_defaults(route_name='rest') class RESTView(object): def __init__(self, request): self.request = request @view_config(request_method='GET') def get(self): return Response('get') @view_config(request_method='POST') def post(self): return Response('post') @view_config(request_method='DELETE') def delete(self): return Response('delete')

Slide 23

Slide 23 text

Class base views - example for decorator haters @view_defaults(route_name='rest') class RESTView(object): def __init__(self, request): self.request = request def get(self): return Response('get') def post(self): return Response('post') def delete(self): return Response('delete') if __name__ == '__main__': config = Configurator() config.add_route('rest', '/rest') config.add_view(RESTView, attr='get', request_method='GET') config.add_view(RESTView, attr='post', request_method='POST') config.add_view(RESTView, attr='delete', request_method='DELETE')

Slide 24

Slide 24 text

Renderers A view don’t always need to return a Response object. If not then Pyramid attempt to use a renderer to construct a response

Slide 25

Slide 25 text

Built-in Renderers - string - JSON - JSONP - Chameleon Template : *.pt or *.txt - Mako Template : *.mak or *.mako

Slide 26

Slide 26 text

Class base views - example with renderer ... @view_defaults(route_name='rest', renderer='JSON') class RESTView(object): def __init__(self, request): self.request = request @view_config(request_method='GET') def get(self): return {'method':'GET'} @view_config(request_method='POST') def post(self): return {'method':'POST'} @view_config(request_method='DELETE') def delete(self): return {'method':'DELETE'}

Slide 27

Slide 27 text

Renderers & Templates - When the renderer is set to a Template. The view needs to return a dict to populate the context renderer='templates/home.pt' renderer='package:templates/home.mak' renderer='home.jinja2'

Slide 28

Slide 28 text

Response adapter To allow views to return objects without requiring a renderer to convert def string_response_adapter(s): response = Response(s) return response config.add_response_adapter(string_response_adapter, str)

Slide 29

Slide 29 text

404 When Pyramid can’t map a URL to a view code, it invokes a not found view from pyramid.view import notfound_view_config @notfound_view_config() def notfound(request): return Response('Not Found, dude', status='404 Not Found') config.add_notfound_view(notfound) Or

Slide 30

Slide 30 text

403 When Pyramid can’t authorize view based on the authorization policy, it invokes a forbidden view from pyramid.view import forbidden_view_config @forbidden_view_config() def forbidden(request): return Response('forbidden') config.add_forbidden_view(forbidden_view) Or

Slide 31

Slide 31 text

HTTP Exceptions HTTP Exceptions can be raised or returned pyramid.httpexceptions.* (200 -> 511) from pyramid.httpexceptions import HTTPNotFound, HTTPForbidden def do_something_notfound(request): return HTTPNotFound() def do_something_forbidden(request): raise HTTPForbidden()

Slide 32

Slide 32 text

Static view Pyramid makes it possible to serve up static asset files from a directory on a filesystem The name represents a URL prefix config.add_static_view(name='static', path='/var/www/static')

Slide 33

Slide 33 text

Missing the good old days? - You can attach view directly to a route config.add_route('home', '/', view= function_view) - You can event use Python dotted name config.add_route('home', '/', view= 'module.function_view')

Slide 34

Slide 34 text

So much more! Events Tweens View predicates Subscriber predicates Callbacks View Mapper Pviews cmd Renderer adapter