summaryrefslogtreecommitdiff
path: root/docs/narr/views.rst
blob: 3331794e32825d717f626c9a08c153bca5d5dfe5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
Views
=====

A view is a callable which is called when a a request enters your
application.  ``repoze.bfg's`` primary job is to find and call a view
when a request reaches it.  The view's return value must implement the
Response object interface.

Defining a View as a Function
-----------------------------

The easiest way to define a view is to create a function that accepts
two arguments: *context*, and *request*.  For example, this is a hello
world view implemented as a function::

  def hello_world(context, request):
      from webob import Response
      return Response('Hello world!')

Defining View Factories
-----------------------

Declarations in your view registry to point at a *view factory*
rather than pointing it at a view implemented as a function.  This
provides an additional level of convenience for some applications.

A view factory, like a view implemented as a function, accepts the
*context* and *request* arguments.  But unlike a view implemented as a
function it returns *another* callable instead of a response.  The
returned callable is then called by ``repoze.bfg``, with its arguments
filled in "magically".

The easiest way to implement a view factory is to imlement it as a
class.  Here's a hello world view factory that is implemented as a
class::

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

      def __call__(self):
          from webob import Response
          return Response('Hello world!')

You can also implement a view factory as a function::

  def HelloWorld(context, request):
      def hello_world():
          from webob import Response
          return Response('Hello world!')
      return hello_world

Using View Factories for Convenience
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View factories aren't just makework.  They exist for convenience,
because, unlike views as functions, views that are returned via a view
factory will have arguments "magically" mapped into them when they are
called by the router.  You can choose the arguments you'd like the
constructed view to receive simply by mentioning each in the signature
of the callable you return from the view factory (or the __call__ of
the class you use as the view factory).

The arguments that are available to be magically mapped into
constructed view calls are as follows.

context

  The current context

request

  The current request

environ

  The current WSGI environment

start_response

  The current  WSGI start_response callable

XXX We need to decide which other elements will be mapped in from the
request and map them in: e.g. query string/form elements, etc.

This means that the ``__call__`` of the following view factory
will have its *environ* and *start_response* arguments filled in
magically during view call time::

  def ViewFactory(object):
      def __init__(self, context, request):
          self.context = context
          self.request = request

      def __call__(self, environ, start_response):
          msg = 'Called via %s ' % environ['PATH_INFO']
          start_response('200 OK', ('Content-Length', len(msg))
          return msg

.. note:: If you're familiar with WSGI, you'll notice above that the
  view factory returns a valid WSGI application.  View
  factories in ``repoze.bfg`` can return any WSGI application.

View Functions Revisited
------------------------

Above we provided an example of a "view" imlemented as a function::

  def hello_world(context, request):
      from webob import Response
      return Response('Hello world!')

When ``repoze.bfg`` finds and calls this callable, has no a-priori
knowledge that would allow it to believe that this function would
return a response directly.  It assumes that what it's calling will
return a *view* instead of a *response*.  In other words, it expects
that everything configured in its view registry points at a view
factory.

However, there is a special case in the logic implemented in the
``repoze.bfg`` router that says if the return value of a view
factory is an object implementing the Response interface, use that
object as the response, and don't try to call the object or magically
map any arguments into it.  Instead, it just passes it along to the
upstream WSGI server.

This is purely for convenience: it's useful to be able to define
simple functions as "views" without the overhead of defining a view
factory.

View Factory Arguments
----------------------

Now that we know what view factories are, what are these *context*
and *request* arguments that are mapped in to it?

context

  An instance of a model found via graph traversal.

request

  A WebOb request object representing the current request.