source: main/waeup.sirp/trunk/src/waeup/sirp/utils/converters.py @ 10982

Last change on this file since 10982 was 7709, checked in by Henrik Bettermann, 13 years ago

When checking conversion we have to omit dictionary fields since there are no dict widgets availiable.

  • Property svn:keywords set to Id
File size: 6.8 KB
RevLine 
[7196]1## $Id: converters.py 7709 2012-02-27 11:53:51Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
[4805]18"""Converters for zope.schema-based datatypes.
19"""
20import grok
[6263]21from zope.component import createObject
[6258]22from zope.formlib import form
[6278]23from zope.formlib.boolwidgets import CheckBoxWidget
[6260]24from zope.formlib.form import (
[6276]25    _widgetKey, WidgetInputError, ValidationError, InputErrors, expandPrefix)
[6278]26from zope.formlib.interfaces import IInputWidget, ISimpleInputWidget
[6276]27from zope.interface import Interface
[6258]28from zope.publisher.browser import TestRequest
[6273]29from waeup.sirp.interfaces import IObjectConverter
[6258]30
[6278]31class ExtendedCheckBoxWidget(CheckBoxWidget):
32    """A checkbox widget that supports more input values as True/False
[7597]33    markers.
[6278]34
35    The default bool widget expects the string 'on' as only valid
36    ``True`` value in HTML forms for bool fields.
37
38    This widget also accepts '1', 'true' and 'yes' for that. Also all
39    uppercase/lowecase combinations of these strings are accepted.
40
41    The widget still renders ``True`` to ``'on'`` when a form is
42    generated.
43    """
44    true_markers = ['1', 'true', 'on', 'yes']
45
46    def _toFieldValue(self, input):
47        """Convert from HTML presentation to Python bool."""
48        if not isinstance(input, basestring):
49            return False
50        return input.lower() in self.true_markers
51
52    def _getFormInput(self):
53        """Returns the form input used by `_toFieldValue`.
54
55        Return values:
56
57          ``'on'``  checkbox is checked
58          ``''``    checkbox is not checked
59          ``None``  form input was not provided
60
61        """
62        value = self.request.get(self.name)
63        if isinstance(value, basestring):
64            value = value.lower()
65        if value in self.true_markers:
66            return 'on'
67        elif self.name + '.used' in self.request:
68            return ''
69        else:
70            return None
71
[6260]72def getWidgetsData(widgets, form_prefix, data):
[6273]73    """Get data and validation errors from `widgets` for `data`.
74
75    Updates the dict in `data` with values from the widgets in
76    `widgets`.
77
78    Returns a list of tuples ``(<WIDGET_NAME>, <ERROR>)`` where
79    ``<WIDGET_NAME>`` is a widget name (normally the same as the
80    associated field name) and ``<ERROR>`` is the exception that
81    happened for that widget/field.
82
83    This is merely a copy from the same-named function in
84    :mod:`zope.formlib.form`. The only difference is that we also
85    store the fieldname for which a validation error happened in the
86    returned error list (what the original does not do).
87
88    """
[6260]89    errors = []
90    form_prefix = expandPrefix(form_prefix)
91
92    for input, widget in widgets.__iter_input_and_widget__():
93        if input and IInputWidget.providedBy(widget):
94            name = _widgetKey(widget, form_prefix)
95
96            if not widget.hasInput():
97                continue
98
99            try:
100                data[name] = widget.getInputValue()
101            except ValidationError, error:
102                # convert field ValidationError to WidgetInputError
103                error = WidgetInputError(widget.name, widget.label, error)
104                errors.append((name, error))
105            except InputErrors, error:
106                errors.append((name, error))
107
108    return errors
109
[6278]110
[6273]111class DefaultObjectConverter(grok.Adapter):
112    """Turn string values into real values.
[6260]113
[6273]114    A converter can convert string values for objects that implement a
115    certain interface into real values based on the given interface.
116    """
[6258]117
[6273]118    grok.context(Interface)
119    grok.provides(IObjectConverter)
[6263]120
[6273]121    def __init__(self, iface):
122        self.iface = iface
[7709]123        # Omit known dictionaries since there is no widget available
124        # for dictionary schema fields
125        self.default_form_fields = form.Fields(iface).omit('description_dict')
[6273]126        return
[6263]127
[6273]128    def fromStringDict(self, data_dict, context, form_fields=None):
129        """Convert values in `data_dict`.
[6263]130
[6273]131        Converts data in `data_dict` into real values based on
132        `context` and `form_fields`.
[6263]133
[6273]134        `data_dict` is a mapping (dict) from field names to values
135        represented as strings.
[6263]136
[6273]137        The fields (keys) to convert can be given in optional
138        `form_fields`. If given, form_fields should be an instance of
139        :class:`zope.formlib.form.Fields`. Suitable instances are for
140        example created by :class:`grok.AutoFields`.
[6263]141
[6273]142        If no `form_fields` are given, a default is computed from the
143        associated interface.
[6263]144
[6273]145        The `context` can be an existing object (implementing the
146        associated interface) or a factory name. If it is a string, we
147        try to create an object using
148        :func:`zope.component.createObject`.
[6263]149
[6273]150        Returns a tuple ``(<FIELD_ERRORS>, <INVARIANT_ERRORS>,
151        <DATA_DICT>)`` where
[6263]152
[6273]153        ``<FIELD_ERRORS>``
154           is a list of tuples ``(<FIELD_NAME>, <ERROR>)`` for each
155           error that happened when validating the input data in
156           `data_dict`
[6258]157
[6273]158        ``<INVARIANT_ERRORS>``
159           is a list of invariant errors concerning several fields
[6258]160
[6273]161        ``<DATA_DICT>``
162           is a dict with the values from input dict converted.
163
164        If errors happen, i.e. the error lists are not empty, always
165        an empty ``<DATA_DICT>`` is returned.
166
[7597]167        If ``<DATA_DICT>`` is non-empty, there were no errors.
[6273]168        """
[6263]169        if form_fields is None:
[6273]170            form_fields = self.default_form_fields
[6263]171
[6273]172        request = TestRequest(form={})
173        for key, val in data_dict.items():
174            request.form['form.%s' % key] = val
175
[6263]176        obj = context
177        if isinstance(context, basestring):
178            obj = createObject(context)
[6273]179
180        widgets = form.setUpInputWidgets(
[6263]181            form_fields, 'form', obj, request)
[6273]182
183        new_data = dict()
184        errors = getWidgetsData(widgets, 'form', new_data)
185
186        invariant_errors = form.checkInvariants(form_fields, new_data)
187        if errors or invariant_errors:
188            err_messages = [(key, err.args[0]) for key, err in errors]
189            invariant_errors = [err.message for err in invariant_errors]
190            return err_messages, invariant_errors, {}
191
192        return errors, invariant_errors, new_data
Note: See TracBrowser for help on using the repository browser.