web/views/forms.py
changeset 5385 b6e250dd7a7d
parent 5368 d321e4b62a10
child 5421 8167de96c523
equal deleted inserted replaced
5382:cb5dfea92285 5385:b6e250dd7a7d
     1 """some base form classes for CubicWeb web client
     1 # organization: Logilab
     2 
     2 # copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
     3 :organization: Logilab
     3 # contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     4 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
     4 # license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     5 """
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     6 Base form classes
       
     7 -----------------
       
     8 
       
     9 .. Note:
       
    10 
       
    11    Form is the glue that bind a context to a set of fields, and is rendered
       
    12    using a form renderer. No display is actually done here, though you'll find
       
    13    some attributes of form that are used to control the rendering process.
       
    14 
       
    15 Besides the automagic form we'll see later, they are barely two form
       
    16 classes in |cubicweb|:
       
    17 
       
    18 .. autoclass:: cubicweb.web.views.forms.FieldsForm
       
    19 .. autoclass:: cubicweb.web.views.forms.EntityFieldsForm
       
    20 
       
    21 As you have probably guessed, choosing between them is easy. Simply ask you the
       
    22 question 'I am editing an entity or not?'. If the answer is yes, use
       
    23 :class:`EntityFieldsForm`, else use :class:`FieldsForm`.
       
    24 
       
    25 Actually there exists a third form class:
       
    26 
       
    27 .. autoclass:: cubicweb.web.views.forms.CompositeForm
       
    28 
       
    29 but you'll use this one rarely.
     7 """
    30 """
     8 __docformat__ = "restructuredtext en"
    31 __docformat__ = "restructuredtext en"
     9 
    32 
    10 from warnings import warn
    33 from warnings import warn
    11 
    34 
    14 from logilab.common.deprecation import deprecated
    37 from logilab.common.deprecation import deprecated
    15 
    38 
    16 from cubicweb import typed_eid
    39 from cubicweb import typed_eid
    17 from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset
    40 from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset
    18 from cubicweb.web import uicfg, form, formwidgets as fwdgs
    41 from cubicweb.web import uicfg, form, formwidgets as fwdgs
    19 from cubicweb.web.formfields import StringField, relvoc_unrelated, guess_field
    42 from cubicweb.web.formfields import relvoc_unrelated, guess_field
    20 
    43 
    21 
    44 
    22 class FieldsForm(form.Form):
    45 class FieldsForm(form.Form):
    23     """base class for fields based forms.
    46     """This is the base class for fields based forms.
       
    47 
       
    48     **Attributes**
    24 
    49 
    25     The following attributes may be either set on subclasses or given on
    50     The following attributes may be either set on subclasses or given on
    26     form selection to customize the generated form:
    51     form selection to customize the generated form:
    27 
    52 
    28     * `needs_js`: sequence of javascript files that should be added to handle
    53     :attr:`needs_js`
    29       this form (through `req.add_js`)
    54       sequence of javascript files that should be added to handle this form
    30 
    55       (through :meth:`~cubicweb.web.request.Request.add_js`)
    31     * `needs_css`: sequence of css files that should be added to handle this
    56 
    32       form (through `req.add_css`)
    57     :attr:`needs_css`
    33 
    58       sequence of css files that should be added to handle this form (through
    34     * `domid`: value for the "id" attribute of the <form> tag
    59       :meth:`~cubicweb.web.request.Request.add_css`)
    35 
    60 
    36     * `action`: value for the "action" attribute of the <form> tag
    61     :attr:`domid`
    37 
    62       value for the "id" attribute of the <form> tag
    38     * `onsubmit`: value for the "onsubmit" attribute of the <form> tag
    63 
    39 
    64     :attr:`action`
    40     * `cssclass`: value for the "class" attribute of the <form> tag
    65       value for the "action" attribute of the <form> tag
    41 
    66 
    42     * `cssstyle`: value for the "style" attribute of the <form> tag
    67     :attr:`onsubmit`
    43 
    68       value for the "onsubmit" attribute of the <form> tag
    44     * `cwtarget`: value for the "cubicweb:target" attribute of the <form> tag
    69 
    45 
    70     :attr:`cssclass`
    46     * `redirect_path`: relative to redirect to after submitting the form
    71       value for the "class" attribute of the <form> tag
    47 
    72 
    48     * `copy_nav_params`: flag telling if navigation paramenters should be copied
    73     :attr:`cssstyle`
    49       back in hidden input
    74       value for the "style" attribute of the <form> tag
    50 
    75 
    51     * `form_buttons`:  form buttons sequence (button widgets instances)
    76     :attr:`cwtarget`
    52 
    77       value for the "cubicweb:target" attribute of the <form> tag
    53     * `form_renderer_id`: id of the form renderer to use to render the form
    78 
    54 
    79     :attr:`redirect_path`
    55     * `fieldsets_in_order`: fieldset name sequence, to control order
    80       relative to redirect to after submitting the form
       
    81 
       
    82     :attr:`copy_nav_params`
       
    83       flag telling if navigation parameters should be copied back in hidden
       
    84       inputs
       
    85 
       
    86     :attr:`form_buttons`
       
    87       sequence of form control (:class:`~cubicweb.web.formwidgets.Button`
       
    88       widgets instances)
       
    89 
       
    90     :attr:`form_renderer_id`
       
    91       identifier of the form renderer to use to render the form
       
    92 
       
    93     :attr:`fieldsets_in_order`
       
    94       sequence of fieldset names , to control order
       
    95 
       
    96     **Generic methods**
       
    97 
       
    98     .. automethod:: cubicweb.web.form.Form.field_by_name(name, role=None)
       
    99     .. automethod:: cubicweb.web.form.Form.fields_by_name(name, role=None)
       
   100 
       
   101     **Form construction methods**
       
   102 
       
   103     .. automethod:: cubicweb.web.form.Form.remove_field(field)
       
   104     .. automethod:: cubicweb.web.form.Form.append_field(field)
       
   105     .. automethod:: cubicweb.web.form.Form.insert_field_before(field, name, role=None)
       
   106     .. automethod:: cubicweb.web.form.Form.insert_field_after(field, name, role=None)
       
   107     .. automethod:: cubicweb.web.form.Form.add_hidden(name, value=None, **kwargs)
       
   108 
       
   109     **Form rendering methods**
       
   110 
       
   111     .. automethod:: cubicweb.web.views.forms.FieldsForm.render
       
   112 
    56     """
   113     """
    57     __regid__ = 'base'
   114     __regid__ = 'base'
    58 
   115 
    59 
   116 
    60     # attributes overrideable by subclasses or through __init__
   117     # attributes overrideable by subclasses or through __init__
    73     @property
   130     @property
    74     def needs_multipart(self):
   131     def needs_multipart(self):
    75         """true if the form needs enctype=multipart/form-data"""
   132         """true if the form needs enctype=multipart/form-data"""
    76         return any(field.needs_multipart for field in self.fields)
   133         return any(field.needs_multipart for field in self.fields)
    77 
   134 
    78     def add_hidden(self, name, value=None, **kwargs):
       
    79         """add an hidden field to the form"""
       
    80         kwargs.setdefault('ignore_req_params', True)
       
    81         kwargs.setdefault('widget', fwdgs.HiddenInput)
       
    82         field = StringField(name=name, value=value, **kwargs)
       
    83         if 'id' in kwargs:
       
    84             # by default, hidden input don't set id attribute. If one is
       
    85             # explicitly specified, ensure it will be set
       
    86             field.widget.setdomid = True
       
    87         self.append_field(field)
       
    88         return field
       
    89 
       
    90     def add_media(self):
   135     def add_media(self):
    91         """adds media (CSS & JS) required by this widget"""
   136         """adds media (CSS & JS) required by this widget"""
    92         if self.needs_js:
   137         if self.needs_js:
    93             self._cw.add_js(self.needs_js)
   138             self._cw.add_js(self.needs_js)
    94         if self.needs_css:
   139         if self.needs_css:
    95             self._cw.add_css(self.needs_css)
   140             self._cw.add_css(self.needs_css)
    96 
   141 
    97     def render(self, formvalues=None, rendervalues=None, renderer=None, **kwargs):
   142     def render(self, formvalues=None, rendervalues=None, renderer=None, **kwargs):
    98         """render this form, using the renderer given in args or the default
   143         """Render this form, using the `renderer` given as argument or the
    99         FormRenderer()
   144         default according to :attr:`form_renderer_id`. The rendered form is
       
   145         returned as an unicode string.
       
   146 
       
   147         `formvalues` is an optional dictionary containing values that will be
       
   148         considered as field's value.
       
   149 
       
   150         Extra keyword arguments will be given to renderer's :meth:`render` method.
       
   151 
       
   152         `rendervalues` is deprecated.
   100         """
   153         """
   101         if rendervalues is not None:
   154         if rendervalues is not None:
   102             warn('[3.6] rendervalues argument is deprecated, all named arguments will be given instead',
   155             warn('[3.6] rendervalues argument is deprecated, all named arguments will be given instead',
   103                  DeprecationWarning, stacklevel=2)
   156                  DeprecationWarning, stacklevel=2)
   104             kwargs = rendervalues
   157             kwargs = rendervalues
   146 
   199 
   147 _AFF = uicfg.autoform_field
   200 _AFF = uicfg.autoform_field
   148 _AFF_KWARGS = uicfg.autoform_field_kwargs
   201 _AFF_KWARGS = uicfg.autoform_field_kwargs
   149 
   202 
   150 class EntityFieldsForm(FieldsForm):
   203 class EntityFieldsForm(FieldsForm):
       
   204     """This class is designed for forms used to edit some entities. It should
       
   205     handle for you all the underlying stuff necessary to properly work with the
       
   206     generic :class:`~cubicweb.web.views.editcontroller.EditController`.
       
   207     """
       
   208 
   151     __regid__ = 'base'
   209     __regid__ = 'base'
   152     __select__ = (match_kwargs('entity')
   210     __select__ = (match_kwargs('entity')
   153                   | (one_line_rset() & non_final_entity()))
   211                   | (one_line_rset() & non_final_entity()))
   154 
   212 
   155     internal_fields = FieldsForm.internal_fields + ('__type', 'eid', '__maineid')
   213     internal_fields = FieldsForm.internal_fields + ('__type', 'eid', '__maineid')
   265     def object_relation_vocabulary(self, rtype, limit=None):
   323     def object_relation_vocabulary(self, rtype, limit=None):
   266         return relvoc_unrelated(self.edited_entity, rtype, 'object', limit=None)
   324         return relvoc_unrelated(self.edited_entity, rtype, 'object', limit=None)
   267 
   325 
   268 
   326 
   269 class CompositeFormMixIn(object):
   327 class CompositeFormMixIn(object):
   270     """form composed of sub-forms"""
       
   271     __regid__ = 'composite'
   328     __regid__ = 'composite'
   272     form_renderer_id = __regid__
   329     form_renderer_id = __regid__
   273 
   330 
   274     def __init__(self, *args, **kwargs):
   331     def __init__(self, *args, **kwargs):
   275         super(CompositeFormMixIn, self).__init__(*args, **kwargs)
   332         super(CompositeFormMixIn, self).__init__(*args, **kwargs)
   285         for form in self.forms:
   342         for form in self.forms:
   286             form.build_context(formvalues)
   343             form.build_context(formvalues)
   287 
   344 
   288 
   345 
   289 class CompositeForm(CompositeFormMixIn, FieldsForm):
   346 class CompositeForm(CompositeFormMixIn, FieldsForm):
   290     pass
   347     """Form composed of sub-forms. Typical usage is edition of multiple entities
       
   348     at once.
       
   349     """
   291 
   350 
   292 class CompositeEntityForm(CompositeFormMixIn, EntityFieldsForm):
   351 class CompositeEntityForm(CompositeFormMixIn, EntityFieldsForm):
   293     pass # XXX why is this class necessary?
   352     pass # XXX why is this class necessary?