web/views/forms.py
changeset 4161 4273f5094651
parent 4159 6b2b20c73d59
child 4162 d2663bcf5306
equal deleted inserted replaced
4160:3fbdeef9a610 4161:4273f5094651
    15 from cubicweb import typed_eid
    15 from cubicweb import typed_eid
    16 from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset
    16 from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset
    17 from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param
    17 from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param
    18 from cubicweb.web import form, formwidgets as fwdgs
    18 from cubicweb.web import form, formwidgets as fwdgs
    19 from cubicweb.web.controller import NAV_FORM_PARAMETERS
    19 from cubicweb.web.controller import NAV_FORM_PARAMETERS
    20 from cubicweb.web.formfields import StringField
    20 from cubicweb.web.formfields import StringField, relvoc_unrelated
    21 
    21 
    22 
    22 
    23 class FieldsForm(form.Form):
    23 class FieldsForm(form.Form):
    24     """base class for fields based forms.
    24     """base class for fields based forms.
    25 
    25 
   171         return self._cw.property_value('ui.default-text-format')
   171         return self._cw.property_value('ui.default-text-format')
   172 
   172 
   173     def form_field_encoding(self, field):
   173     def form_field_encoding(self, field):
   174         """return encoding used for the given (text) field"""
   174         """return encoding used for the given (text) field"""
   175         return self._cw.encoding
   175         return self._cw.encoding
   176 
       
   177     def form_field_vocabulary(self, field, limit=None):
       
   178         """return vocabulary for the given field. Should be overriden in
       
   179         specific forms using fields which requires some vocabulary
       
   180         """
       
   181         raise NotImplementedError
       
   182 
   176 
   183     def form_field_modified(self, field):
   177     def form_field_modified(self, field):
   184         return field.is_visible()
   178         return field.is_visible()
   185 
   179 
   186     def _field_has_error(self, field):
   180     def _field_has_error(self, field):
   255         """return true if the field has some error in given validation exception
   249         """return true if the field has some error in given validation exception
   256         """
   250         """
   257         return super(EntityFieldsForm, self)._field_has_error(field) \
   251         return super(EntityFieldsForm, self)._field_has_error(field) \
   258                and self.form_valerror.eid == self.edited_entity.eid
   252                and self.form_valerror.eid == self.edited_entity.eid
   259 
   253 
   260     def _relation_vocabulary(self, rtype, targettype, role,
       
   261                             limit=None, done=None):
       
   262         """return unrelated entities for a given relation and target entity type
       
   263         for use in vocabulary
       
   264         """
       
   265         if done is None:
       
   266             done = set()
       
   267         rset = self.edited_entity.unrelated(rtype, targettype, role, limit)
       
   268         res = []
       
   269         for entity in rset.entities():
       
   270             if entity.eid in done:
       
   271                 continue
       
   272             done.add(entity.eid)
       
   273             res.append((entity.view('combobox'), entity.eid))
       
   274         return res
       
   275 
       
   276     def _req_display_value(self, field):
       
   277         value = super(EntityFieldsForm, self)._req_display_value(field)
       
   278         if value is None:
       
   279             value = self.edited_entity.linked_to(field.name, field.role)
       
   280             if value:
       
   281                 searchedvalues = ['%s:%s:%s' % (field.name, eid, field.role)
       
   282                                   for eid in value]
       
   283                 # remove associated __linkto hidden fields
       
   284                 for field in self.root_form.fields_by_name('__linkto'):
       
   285                     if field.initial in searchedvalues:
       
   286                         self.root_form.remove_field(field)
       
   287             else:
       
   288                 value = None
       
   289         return value
       
   290 
       
   291     def _form_field_default_value(self, field, load_bytes):
       
   292         defaultattr = 'default_%s' % field.name
       
   293         if hasattr(self.edited_entity, defaultattr):
       
   294             # XXX bw compat, default_<field name> on the entity
       
   295             warn('found %s on %s, should be set on a specific form'
       
   296                  % (defaultattr, self.edited_entity.__regid__), DeprecationWarning)
       
   297             value = getattr(self.edited_entity, defaultattr)
       
   298             if callable(value):
       
   299                 value = value()
       
   300         else:
       
   301             value = super(EntityFieldsForm, self).form_field_value(field,
       
   302                                                                    load_bytes)
       
   303         return value
       
   304 
       
   305     def form_default_renderer(self):
       
   306         return self._cw.vreg['formrenderers'].select(
   254         return self._cw.vreg['formrenderers'].select(
   307             self.form_renderer_id, self._cw, rset=self.cw_rset, row=self.cw_row,
   255             self.form_renderer_id, self._cw, rset=self.cw_rset, row=self.cw_row,
   308             col=self.cw_col, entity=self.edited_entity)
   256             col=self.cw_col, entity=self.edited_entity)
   309 
   257 
   310 
   258 
   323         if field.eidparam and entity.e_schema.has_metadata(field.name, 'encoding') and (
   271         if field.eidparam and entity.e_schema.has_metadata(field.name, 'encoding') and (
   324             entity.has_eid() or '%s_encoding' % field.name in entity):
   272             entity.has_eid() or '%s_encoding' % field.name in entity):
   325             return self.edited_entity.attr_metadata(field.name, 'encoding')
   273             return self.edited_entity.attr_metadata(field.name, 'encoding')
   326         return super(EntityFieldsForm, self).form_field_encoding(field)
   274         return super(EntityFieldsForm, self).form_field_encoding(field)
   327     # XXX all this vocabulary handling should be on the field, no?
   275     # XXX all this vocabulary handling should be on the field, no?
   328 
       
   329     def form_field_vocabulary(self, field, limit=None):
       
   330         """return vocabulary for the given field"""
       
   331         role, rtype = field.role, field.name
       
   332         method = '%s_%s_vocabulary' % (role, rtype)
       
   333     def actual_eid(self, eid):
   276     def actual_eid(self, eid):
   334         # should be either an int (existant entity) or a variable (to be
   277         # should be either an int (existant entity) or a variable (to be
   335         # created entity)
   278         # created entity)
   336         assert eid or eid == 0, repr(eid) # 0 is a valid eid
   279         assert eid or eid == 0, repr(eid) # 0 is a valid eid
   337         try:
   280         try:
   338             vocabfunc = getattr(self, method)
       
   339         except AttributeError:
       
   340             return typed_eid(eid)
   281             return typed_eid(eid)
   341         except ValueError:
   282         except ValueError:
   342             try:
   283             try:
   343                 # XXX bw compat, <role>_<rtype>_vocabulary on the entity
       
   344                 vocabfunc = getattr(self.edited_entity, method)
       
   345             except AttributeError:
       
   346                 vocabfunc = getattr(self, '%s_relation_vocabulary' % role)
       
   347             else:
       
   348                 warn('found %s on %s, should be set on a specific form'
       
   349                      % (method, self.edited_entity.__regid__), DeprecationWarning)
       
   350         # NOTE: it is the responsibility of `vocabfunc` to sort the result
       
   351         #       (direclty through RQL or via a python sort). This is also
       
   352         #       important because `vocabfunc` might return a list with
       
   353         #       couples (label, None) which act as separators. In these
       
   354         #       cases, it doesn't make sense to sort results afterwards.
       
   355         return vocabfunc(rtype, limit)
       
   356 
   284 
   357     def form_field_modified(self, field):
   285     def form_field_modified(self, field):
   358         if field.is_visible():
   286         if field.is_visible():
   359             # fields not corresponding to an entity attribute / relations
   287             # fields not corresponding to an entity attribute / relations
   360             # are considered modified
   288             # are considered modified
   377             new_value = field.process_form_value(self)
   305             new_value = field.process_form_value(self)
   378             if self.edited_entity.has_eid() and (previous_value == new_value):
   306             if self.edited_entity.has_eid() and (previous_value == new_value):
   379                 return False # not modified
   307                 return False # not modified
   380             return True
   308             return True
   381         return False
   309         return False
   382 
       
   383     def subject_relation_vocabulary(self, rtype, limit=None):
       
   384         """defaut vocabulary method for the given relation, looking for
       
   385         relation's object entities (i.e. self is the subject)
       
   386         """
       
   387         entity = self.edited_entity
       
   388         if isinstance(rtype, basestring):
       
   389             rtype = self._cw.vreg.schema.rschema(rtype)
       
   390         done = None
       
   391         assert not rtype.final, rtype
       
   392         if entity.has_eid():
       
   393             done = set(e.eid for e in getattr(entity, str(rtype)))
       
   394         result = []
       
   395         rsetsize = None
       
   396         for objtype in rtype.objects(entity.e_schema):
       
   397             if limit is not None:
       
   398                 rsetsize = limit - len(result)
       
   399             result += self._relation_vocabulary(rtype, objtype, 'subject',
       
   400                                                 rsetsize, done)
       
   401             if limit is not None and len(result) >= limit:
       
   402                 break
       
   403         return result
       
   404 
       
   405     def object_relation_vocabulary(self, rtype, limit=None):
       
   406         """defaut vocabulary method for the given relation, looking for
       
   407         relation's subject entities (i.e. self is the object)
       
   408         """
       
   409         entity = self.edited_entity
       
   410         if isinstance(rtype, basestring):
       
   411             rtype = self._cw.vreg.schema.rschema(rtype)
       
   412         done = None
       
   413         if entity.has_eid():
       
   414             done = set(e.eid for e in getattr(entity, 'reverse_%s' % rtype))
       
   415         result = []
       
   416         rsetsize = None
       
   417         for subjtype in rtype.subjects(entity.e_schema):
       
   418             if limit is not None:
       
   419                 rsetsize = limit - len(result)
       
   420             result += self._relation_vocabulary(rtype, subjtype, 'object',
       
   421                                                 rsetsize, done)
       
   422             if limit is not None and len(result) >= limit:
       
   423                 break
       
   424         return result
       
   425                 return self._cw.data['eidmap'][eid]
   310                 return self._cw.data['eidmap'][eid]
   426             except KeyError:
   311             except KeyError:
   427                 self._cw.data['eidmap'][eid] = None
   312                 self._cw.data['eidmap'][eid] = None
   428                 return None
   313                 return None
   429 
   314 
   431         return ()
   316         return ()
   432 
   317 
   433     def should_display_add_new_relation_link(self, rschema, existant, card):
   318     def should_display_add_new_relation_link(self, rschema, existant, card):
   434         return False
   319         return False
   435 
   320 
       
   321     @deprecated('[3.6] use cw.web.formfields.relvoc_unrelated function')
       
   322     def subject_relation_vocabulary(self, rtype, limit=None):
       
   323         """defaut vocabulary method for the given relation, looking for
       
   324         relation's object entities (i.e. self is the subject)
       
   325         """
       
   326         return relvoc_unrelated(self.edited_entity, rtype, 'subject', limit=None)
       
   327 
       
   328     @deprecated('[3.6] use cw.web.formfields.relvoc_unrelated function')
       
   329     def object_relation_vocabulary(self, rtype, limit=None):
       
   330         return relvoc_unrelated(self.edited_entity, rtype, 'object', limit=None)
       
   331 
   436 
   332 
   437 class CompositeFormMixIn(object):
   333 class CompositeFormMixIn(object):
   438     """form composed of sub-forms"""
   334     """form composed of sub-forms"""
   439     __regid__ = 'composite'
   335     __regid__ = 'composite'
   440     form_renderer_id = __regid__
   336     form_renderer_id = __regid__