web/views/forms.py
changeset 7584 e1881933f366
parent 7583 6632c762cd63
child 7586 b3688b15d7f5
equal deleted inserted replaced
7583:6632c762cd63 7584:e1881933f366
     1 # copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     3 #
     3 #
     4 # This file is part of CubicWeb.
     4 # This file is part of CubicWeb.
     5 #
     5 #
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
    43 """
    43 """
    44 __docformat__ = "restructuredtext en"
    44 __docformat__ = "restructuredtext en"
    45 
    45 
    46 from warnings import warn
    46 from warnings import warn
    47 
    47 
    48 from logilab.common import dictattr
    48 from logilab.common import dictattr, tempattr
    49 from logilab.common.decorators import iclassmethod
    49 from logilab.common.decorators import iclassmethod
    50 from logilab.common.compat import any
    50 from logilab.common.compat import any
       
    51 from logilab.common.textutils import splitstrip
    51 from logilab.common.deprecation import deprecated
    52 from logilab.common.deprecation import deprecated
    52 
    53 
    53 from cubicweb import typed_eid
    54 from cubicweb import ValidationError, typed_eid
    54 from cubicweb.utils import support_args
    55 from cubicweb.utils import support_args
    55 from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset
    56 from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset
       
    57 from cubicweb.web import RequestError, ProcessFormError
    56 from cubicweb.web import uicfg, form, formwidgets as fwdgs
    58 from cubicweb.web import uicfg, form, formwidgets as fwdgs
    57 from cubicweb.web.formfields import relvoc_unrelated, guess_field
    59 from cubicweb.web.formfields import relvoc_unrelated, guess_field
    58 
    60 
    59 
    61 
    60 class FieldsForm(form.Form):
    62 class FieldsForm(form.Form):
   123 
   125 
   124     **Form rendering methods**
   126     **Form rendering methods**
   125 
   127 
   126     .. automethod:: cubicweb.web.views.forms.FieldsForm.render
   128     .. automethod:: cubicweb.web.views.forms.FieldsForm.render
   127 
   129 
       
   130     **Form posting methods**
       
   131 
       
   132     Once a form is posted, you can retrieve the form on the controller side and
       
   133     use the following methods to ease processing. For "simple" forms, this
       
   134     should looks like :
       
   135 
       
   136     .. sourcecode :: python
       
   137 
       
   138         form = self._cw.vreg['forms'].select('myformid', self._cw)
       
   139         posted = form.process_posted()
       
   140         # do something with the returned dictionary
       
   141 
       
   142     Notice that form related to entity edition should usually use the
       
   143     `edit` controller which will handle all the logic for you.
       
   144 
       
   145     .. automethod:: cubicweb.web.views.forms.FieldsForm.process_content
       
   146     .. automethod:: cubicweb.web.views.forms.FieldsForm.iter_modified_fields
   128     """
   147     """
   129     __regid__ = 'base'
   148     __regid__ = 'base'
   130 
   149 
   131 
   150 
   132     # attributes overrideable by subclasses or through __init__
   151     # attributes overrideable by subclasses or through __init__
   216         # use a copy in case fields are modified while context is built (eg
   235         # use a copy in case fields are modified while context is built (eg
   217         # __linkto handling for instance)
   236         # __linkto handling for instance)
   218         for field in self.fields[:]:
   237         for field in self.fields[:]:
   219             for field in field.actual_fields(self):
   238             for field in field.actual_fields(self):
   220                 field.form_init(self)
   239                 field.form_init(self)
       
   240         # store used field in an hidden input for later usage by a controller
       
   241         fields = set()
       
   242         eidfields = set()
       
   243         for field in self.fields:
       
   244             if field.eidparam:
       
   245                 eidfields.add(field.role_name())
       
   246             elif field.name not in self.control_fields:
       
   247                 fields.add(field.role_name())
       
   248         if fields:
       
   249             self.add_hidden('_cw_fields', u','.join(fields))
       
   250         if eidfields:
       
   251             self.add_hidden('_cw_entity_fields', u','.join(eidfields),
       
   252                             eidparam=True)
   221 
   253 
   222     _default_form_action_path = 'edit'
   254     _default_form_action_path = 'edit'
   223     def form_action(self):
   255     def form_action(self):
   224         try:
   256         try:
   225             action = self.get_action() # avoid spurious warning w/ autoform bw compat property
   257             action = self.get_action() # avoid spurious warning w/ autoform bw compat property
   226         except AttributeError:
   258         except AttributeError:
   227             action = self.action
   259             action = self.action
   228         if action is None:
   260         if action is None:
   229             return self._cw.build_url(self._default_form_action_path)
   261             return self._cw.build_url(self._default_form_action_path)
   230         return action
   262         return action
       
   263 
       
   264     # controller form processing methods #######################################
       
   265 
       
   266     def iter_modified_fields(self, editedfields=None, entity=None):
       
   267         """return a generator on field that has been modified by the posted
       
   268         form.
       
   269         """
       
   270         if editedfields is None:
       
   271             try:
       
   272                 editedfields = self._cw.form['_cw_fields']
       
   273             except KeyError:
       
   274                 raise RequestError(self._cw._('no edited fields specified'))
       
   275         entityform = entity and self.field_by_name.im_func.func_code.co_argcount == 4 # XXX
       
   276         for editedfield in splitstrip(editedfields):
       
   277             try:
       
   278                 name, role = editedfield.split('-')
       
   279             except:
       
   280                 name = editedfield
       
   281                 role = None
       
   282             if entityform:
       
   283                 field = self.field_by_name(name, role, eschema=entity.e_schema)
       
   284             else:
       
   285                 field = self.field_by_name(name, role)
       
   286             if field.has_been_modified(self):
       
   287                 yield field
       
   288 
       
   289     def process_posted(self):
       
   290         """use this method to process the content posted by a simple form.  it
       
   291         will return a dictionary with field names as key and typed value as
       
   292         associated value.
       
   293         """
       
   294         with tempattr(self, 'formvalues', {}): # init fields value cache
       
   295             errors = []
       
   296             processed = {}
       
   297             for field in self.iter_modified_fields():
       
   298                 try:
       
   299                     for field, value in field.process_posted(self):
       
   300                         processed[field.role_name()] = value
       
   301                 except ProcessFormError, exc:
       
   302                     errors.append((field, exc))
       
   303             if errors:
       
   304                 errors = dict((f.role_name(), unicode(ex)) for f, ex in errors)
       
   305                 raise ValidationError(None, errors)
       
   306             return processed
   231 
   307 
   232     @deprecated('[3.6] use .add_hidden(name, value, **kwargs)')
   308     @deprecated('[3.6] use .add_hidden(name, value, **kwargs)')
   233     def form_add_hidden(self, name, value=None, **kwargs):
   309     def form_add_hidden(self, name, value=None, **kwargs):
   234         return self.add_hidden(name, value, **kwargs)
   310         return self.add_hidden(name, value, **kwargs)
   235 
   311 
   321             return '%s#%s' % (self.edited_entity.absolute_url(), self.domid)
   397             return '%s#%s' % (self.edited_entity.absolute_url(), self.domid)
   322         # XXX we should not consider some url parameters that may lead to
   398         # XXX we should not consider some url parameters that may lead to
   323         # different url after a validation error
   399         # different url after a validation error
   324         return '%s#%s' % (self._cw.url(), self.domid)
   400         return '%s#%s' % (self._cw.url(), self.domid)
   325 
   401 
   326     def build_context(self, formvalues=None):
       
   327         if self.formvalues is not None:
       
   328             return # already built
       
   329         super(EntityFieldsForm, self).build_context(formvalues)
       
   330         edited = set()
       
   331         for field in self.fields:
       
   332             if field.eidparam:
       
   333                 edited.add(field.role_name())
       
   334         self.add_hidden('_cw_edited_fields', u','.join(edited), eidparam=True)
       
   335 
       
   336     def default_renderer(self):
   402     def default_renderer(self):
   337         return self._cw.vreg['formrenderers'].select(
   403         return self._cw.vreg['formrenderers'].select(
   338             self.form_renderer_id, self._cw, rset=self.cw_rset, row=self.cw_row,
   404             self.form_renderer_id, self._cw, rset=self.cw_rset, row=self.cw_row,
   339             col=self.cw_col, entity=self.edited_entity)
   405             col=self.cw_col, entity=self.edited_entity)
   340 
   406