3
Common decorators intended for use in controllers. Additional
4
decorators for use with controllers are in the
5
:mod:`~pylons.decorators.cache`, :mod:`~pylons.decorators.rest` and
6
:mod:`~pylons.decorators.secure` modules.
15
from decorator import decorator
16
from formencode import api, htmlfill, variabledecode
17
from webob.multidict import UnicodeMultiDict
19
from pylons.decorators.util import get_pylons
20
from pylons.i18n import _ as pylons_gettext
22
__all__ = ['jsonify', 'validate']
24
log = logging.getLogger(__name__)
27
def jsonify(func, *args, **kwargs):
28
"""Action decorator that formats output for JSON
30
Given a function that will return content, this decorator will turn
31
the result into JSON, with a content-type of 'application/json' and
35
pylons = get_pylons(args)
36
pylons.response.headers['Content-Type'] = 'application/json'
37
data = func(*args, **kwargs)
38
if isinstance(data, (list, tuple)):
39
msg = "JSON responses with Array envelopes are susceptible to " \
40
"cross-site data leak attacks, see " \
41
"http://pylonshq.com/warnings/JSONArray"
42
warnings.warn(msg, Warning, 2)
44
log.debug("Returning JSON wrapped action output")
45
return simplejson.dumps(data)
48
def validate(schema=None, validators=None, form=None, variable_decode=False,
49
dict_char='.', list_char='-', post_only=True, state=None,
50
on_get=False, **htmlfill_kwargs):
51
"""Validate input either for a FormEncode schema, or individual
54
Given a form schema or dict of validators, validate will attempt to
55
validate the schema or validator list.
57
If validation was successful, the valid result dict will be saved
58
as ``self.form_result``. Otherwise, the action will be re-run as if
59
it was a GET, and the output will be filled by FormEncode's
60
htmlfill to fill in the form field errors.
63
Refers to a FormEncode Schema object to use during validation.
65
Method used to display the form, which will be used to get the
66
HTML representation of the form for error filling.
68
Boolean to indicate whether FormEncode's variable decode
69
function should be run on the form input before validation.
71
Passed through to FormEncode. Toggles the form field naming
72
scheme used to determine what is used to represent a dict. This
73
option is only applicable when used with variable_decode=True.
75
Passed through to FormEncode. Toggles the form field naming
76
scheme used to determine what is used to represent a list. This
77
option is only applicable when used with variable_decode=True.
79
Boolean that indicates whether or not GET (query) variables
80
should be included during validation.
83
``post_only`` applies to *where* the arguments to be
84
validated come from. It does *not* restrict the form to
85
only working with post, merely only checking POST vars.
87
Passed through to FormEncode for use in validators that utilize
90
Whether to validate on GET requests. By default only POST
91
requests are validated.
95
class SomeController(BaseController):
98
return render('/myform.mako')
100
@validate(schema=model.forms.myshema(), form='create')
101
def update(self, id):
102
# Do something with self.form_result
107
state = PylonsFormEncodeState
108
def wrapper(func, self, *args, **kwargs):
109
"""Decorator Wrapper function"""
110
request = self._py_object.request
113
# Skip the validation if on_get is False and its a GET
114
if not on_get and request.environ['REQUEST_METHOD'] == 'GET':
115
return func(self, *args, **kwargs)
117
# If they want post args only, use just the post args
119
params = request.POST
121
params = request.params
123
params = params.mixed()
125
log.debug("Running variable_decode on params")
126
decoded = variabledecode.variable_decode(params, dict_char,
132
log.debug("Validating against a schema")
134
self.form_result = schema.to_python(decoded, state)
135
except formencode.Invalid, e:
136
errors = e.unpack_errors(variable_decode, dict_char, list_char)
138
log.debug("Validating against provided validators")
139
if isinstance(validators, dict):
140
if not hasattr(self, 'form_result'):
141
self.form_result = {}
142
for field, validator in validators.iteritems():
144
self.form_result[field] = \
145
validator.to_python(decoded.get(field), state)
146
except formencode.Invalid, error:
147
errors[field] = error
149
log.debug("Errors found in validation, parsing form with htmlfill "
151
request.environ['REQUEST_METHOD'] = 'GET'
152
self._py_object.tmpl_context.form_errors = errors
154
# If there's no form supplied, just continue with the current
157
return func(self, *args, **kwargs)
159
request.environ['pylons.routes_dict']['action'] = form
160
response = self._dispatch_call()
162
# If the form_content is an exception response, return it
163
if hasattr(response, '_exception'):
166
htmlfill_kwargs2 = htmlfill_kwargs.copy()
167
htmlfill_kwargs2.setdefault('encoding', request.charset)
168
return htmlfill.render(response, defaults=params, errors=errors,
170
return func(self, *args, **kwargs)
171
return decorator(wrapper)
174
def pylons_formencode_gettext(value):
175
"""Translates a string ``value`` using pylons gettext first and if
176
that fails, formencode gettext.
178
This allows to "merge" localized error messages from built-in
179
FormEncode's validators with application-specific validators.
182
trans = pylons_gettext(value)
184
# translation failed, try formencode
185
trans = api._stdtrans(value)
189
class PylonsFormEncodeState(object):
190
"""A ``state`` for FormEncode validate API that includes smart
193
The FormEncode library used by validate() decorator has some
194
provision for localizing error messages. In particular, it looks
195
for attribute ``_`` in the application-specific state object that
196
gets passed to every ``.to_python()`` call. If it is found, the
197
``_`` is assumed to be a gettext-like function and is called to
198
localize error messages.
200
One complication is that FormEncode ships with localized error
201
messages for standard validators so the user may want to re-use
202
them instead of gathering and translating everything from scratch.
203
To allow this, we pass as ``_`` a function which looks up
204
translation both in application and formencode message catalogs.
207
_ = staticmethod(pylons_formencode_gettext)