forked from morepath/morepath
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathview.py
More file actions
182 lines (143 loc) · 6.53 KB
/
view.py
File metadata and controls
182 lines (143 loc) · 6.53 KB
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
"""Rendering views.
A view is a function that returns something. This can be a
:class:`morepath.Response`, but it can also be a structure (such a
dict) that should be rendered *to* a response. If the view is a JSON
view this dumps this structure as JSON. If the view is a HTML view
this structure can be converted to HTML using a template.
:func:`morepath.render_json`, :func:`morepath.render_html` and
:func:`morepath.redirect` are members of the public API.
See also :class:`morepath.directive.ViewRegistry`
"""
import json
from webob.exc import HTTPFound, HTTPNotFound, HTTPForbidden
from webob import Response as BaseResponse
from .request import Response
from .template import TemplateEngineRegistry
class View(object):
"""A view as registered in the view registry.
:param func: view function. Given a model instance and a request
argument, this function must return either a structure that can
be turned into a response or a response.
:param render: a function used to render view function return value
as a response.
:param permission: permission class that the identity must have
according to permission rules. If the view doesn't have the
permission access is forbidden.
:param internal: bool to indicate whether this view is internal.
If the view is internal you can use it with
:meth:`morepath.Request.view` but it doesn't have a URL
and will be 404 Not Found.
"""
def __init__(self, func, render, permission, internal):
self.func = func
self.render = render
self.permission = permission
self.internal = internal
def __call__(self, app, obj, request):
"""Render a model instance.
If view is internal it cannot be rendered.
If the identity does not have the permission for
this object according to the permission rules then
:class:`webob.exc.HTTPForbidden` is raised.
Any functions specified using :meth:`morepath.Request.after`
are run against the response once it is created, if that
response is not an error.
:param obj: the model instance
:param request: the request
:return: A :class:`webob.response.Response` instance.
"""
if self.internal:
raise HTTPNotFound()
if self.permission is not None and\
not request.app._permits(request.identity, obj, self.permission):
raise HTTPForbidden()
content = self.func(obj, request)
if isinstance(content, BaseResponse):
# the view took full control over the response
response = content
else:
response = self.render(content, request)
# run request after if it's a 2XX or 3XX response
if 200 <= response.status_code <= 399:
request.run_after(response)
return response
def render_view(content, request):
"""Default render function for view if none was supplied.
This just assumes the content is a string and renders it into
a response.
:param content: content as returned by view function.
:param request: request object
:return: a response instance with the content.
"""
return Response(content, content_type='text/plain')
class ViewRegistry(object):
"""A registry of views.
:param template_engine_registry: a
:class:`morepath.directive.TemplateEngineRegistry` used to render
templated views.
"""
factory_arguments = {
'template_engine_registry': TemplateEngineRegistry,
}
app_class_arg = True
def __init__(self, template_engine_registry, app_class):
self.template_engine_registry = template_engine_registry
self.app_class = app_class
def predicate_key(self, key_dict):
"""Given a dictionary create a unique predicate key.
See also :meth:`reg.Registry.key_dict_to_predicate_key`.
:param key_dict: a dict containing view registration info,
for instance model, request_method, etc.
:result: an immutable object representing the predicate.
"""
return self.app_class.get_view.key_dict_to_predicate_key(key_dict)
def register_view(self, key_dict, view,
render=render_view,
template=None,
permission=None,
internal=False):
"""Register a view.
:param key_dict: a dict containing view registration info,
for instance model, request_method, etc.
:param view: the view function as registered.
:param render_view: the render function to use to turn the view
function result value into a response. By default the content
is interpreted as a string body.
:param template: the template used to render the view function
result value into a response. By default there is no template.
:param permission: the permission class used to check whether the
identity is permitted to use the view on the model instance.
:param internal: whether this view is internal or not.
"""
if template is not None:
render = self.template_engine_registry.get_template_render(
template, render)
v = View(view, render, permission, internal)
self.app_class.get_view.register(v, **key_dict)
def render_json(content, request):
"""Take dict/list/string/number content and return json response.
This respects the :meth:`morepath.App.dump_json` directive that
can be used to serialize any object to JSON. By default this
serializes Python objects like dicts, strings to JSON.
:param content: content as returned from view function.
:param request: a :class:`morepath.Request` instance.
:return: a :class:`morepath.Response` instance with a serialized
JSON body.
"""
return Response(json.dumps(request.app._dump_json(content, request)),
content_type='application/json')
def render_html(content, request):
"""Take string and return text/html response.
:param content: contnet as returned from view function.
:param request: a :class:`morepath.Request` instance.
:return: a :class:`morepath.Response` instance with ``content``
as the body.
"""
return Response(content, content_type='text/html')
def redirect(location):
"""Return a response object that redirects to location.
:param location: a URL to redirect to.
:return: a :class:`webob.exc.HTTPFound` response object. You can
return this from a view to redirect.
"""
return HTTPFound(location=location)