root/sandbox/PylonsGenshi/trunk/pylonsgenshi/decorators.py

Revision 78, 6.2 kB (checked in by s0undt3ch, 7 months ago)

Final update on doc, needed to fix trac itself.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Id URL LastChangedDate Rev LastChangedBy
Line 
1# -*- coding: utf-8 -*-
2# vim: sw=4 ts=4 fenc=utf-8
3# =============================================================================
4# $Id$
5# =============================================================================
6#             $URL$
7# $LastChangedDate$
8#             $Rev$
9#   $LastChangedBy$
10# =============================================================================
11# Copyright (C) 2007 UfSoft.org - Pedro Algarvio <ufs@ufsoft.org>
12#
13# Please view LICENSE for additional licensing information.
14# =============================================================================
15"""
16Genshi related decorators ported from pylons.
17"""
18import os
19import pylons
20from paste.util.multidict import UnicodeMultiDict
21import formencode
22import formencode.variabledecode as variabledecode
23from decorator import decorator
24from pylons.decorators import determine_response_charset
25from genshi.filters.html import HTMLFormFiller
26from genshi.template.loader import TemplateLoader
27import logging
28
29log = logging.getLogger(__name__)
30
31__docformat__ = 'restructuredtext en'
32
33def validate(template=None, schema=None, validators=None, form=None,
34             variable_decode=False, dict_char='.', list_char='-', state=None,
35             post_only=True, on_get=False):
36    """Validate input either for a FormEncode schema, or individual validators
37
38    Given a form schema or dict of validators, validate will attempt to
39    validate the schema or validator list.
40
41    If validation was successful, the valid result dict will be saved
42    as ``self.form_result``. Otherwise, the action will be re-run as if it was
43    a GET, and the output will be filled by FormEncode's htmlfill to fill in
44    the form field errors.
45
46    ``template``
47        Refers to the Genshi template to use in case errors need to be shown.
48    ``schema``
49        Refers to a FormEncode Schema object to use during validation.
50    ``form``
51        Method used to display the form, which will be used to get the
52        HTML representation of the form for error filling.
53    ``variable_decode``
54        Boolean to indicate whether FormEncode's variable decode function
55        should be run on the form input before validation.
56    ``dict_char``
57        Passed through to FormEncode. Toggles the form field naming
58        scheme used to determine what is used to represent a dict. This
59        option is only applicable when used with variable_decode=True.
60    ``list_char``
61        Passed through to FormEncode. Toggles the form field naming
62        scheme used to determine what is used to represent a list. This
63        option is only applicable when used with variable_decode=True.
64    ``post_only``
65        Boolean that indicates whether or not GET (query) variables should
66        be included during validation.
67
68        .. warning::
69            ``post_only`` applies to *where* the arguments to be
70            validated come from. It does *not* restrict the form to only
71            working with post, merely only checking POST vars.
72    ``state``
73        Passed through to FormEncode for use in validators that utilize
74        a state object.
75    ``on_get``
76        Whether to validate on GET requests. By default only POST requests
77        are validated.
78
79    Example:
80
81    .. code-block:: python
82
83        class SomeController(BaseController):
84
85            def create(self, id):
86                return render('myform.html')
87
88            @validate(template='myform.html', schema=model.forms.myshema(),
89                      form='create')
90            def update(self, id):
91                # Do something with self.form_result
92                pass
93    """
94    log.debug('On PylonsGenshi validate decorator')
95    def wrapper(func, self, *args, **kwargs):
96        """Decorator Wrapper function"""
97        request = pylons.request._current_obj()
98        errors = {}
99         # Skip the validation if on_get is False and its a GET
100        if not on_get and request.environ['REQUEST_METHOD'] == 'GET':
101            return func(self, *args, **kwargs)
102
103        if post_only:
104            params = request.POST
105        else:
106            params = request.params
107        is_unicode_params = isinstance(params, UnicodeMultiDict)
108        params = params.mixed()
109        if variable_decode:
110            log.debug("Running variable_decode on params:")
111            decoded = variabledecode.variable_decode(params, dict_char,
112                                                     list_char)
113            log.debug(decoded)
114        else:
115            decoded = params
116
117        if schema:
118            log.debug("Validating against a schema")
119            try:
120                self.form_result = schema.to_python(decoded, state)
121            except formencode.Invalid, e:
122                errors = e.unpack_errors(variable_decode, dict_char, list_char)
123        if validators:
124            log.debug("Validating against provided validators")
125            if isinstance(validators, dict):
126                if not hasattr(self, 'form_result'):
127                    self.form_result = {}
128                for field, validator in validators.iteritems():
129                    try:
130                        self.form_result[field] = \
131                            validator.to_python(decoded.get(field), state)
132                    except formencode.Invalid, error:
133                        errors[field] = error
134        if errors:
135            log.debug("Errors found in validation, parsing form with htmlfill "
136                      "for errors")
137            request.environ['REQUEST_METHOD'] = 'GET'
138            pylons.c.form_errors = errors
139
140            # If there's no form supplied, just continue with the current
141            # function call.
142            if not form:
143                raise Exception('You MUST pass a form to display errors')
144                return func(self, *args, **kwargs)
145
146            request.environ['pylons.routes_dict']['action'] = form
147            response = self._dispatch_call()
148
149            log.debug(errors)
150            pylons.c.errors = errors
151            engine_dict = pylons.buffet._update_names({})
152            loader = TemplateLoader(pylons.config['pylons.paths']['templates'])
153            tpl = loader.load(template.replace('.', os.sep)+'.html')
154            stream = tpl.generate(**engine_dict) | HTMLFormFiller(data=decoded)
155
156            return  stream.render()
157        return func(self, *args, **kwargs)
158    return decorator(wrapper)
Note: See TracBrowser for help on using the browser.