Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1"""Field.""" 

2# Standard Library 

3import itertools 

4import re 

5import unicodedata 

6import weakref 

7 

8# Pyramid 

9import colander 

10from chameleon.utils import Markup 

11import peppercorn 

12 

13# Deform 

14from deform.widget import HiddenWidget 

15 

16from . import compat 

17from . import decorator 

18from . import exception 

19from . import schema 

20from . import template 

21from . import widget 

22 

23 

24class _Marker(object): 

25 def __repr__(self): # pragma: no cover 

26 return "(Default)" 

27 

28 __str__ = __repr__ 

29 

30 

31_marker = _Marker() 

32 

33 

34class Field(object): 

35 """Represents an individual form field (a visible object in a 

36 form rendering). 

37 

38 A :class:`deform.form.Field` object instance is meant to last for 

39 the duration of a single web request. As a result, a field object 

40 is often used as a scratchpad by the widget associated with that 

41 field. Using a field as a scratchpad makes it possible to build 

42 implementations of state-retaining widgets while instances of 

43 those widget still only need to be constructed once instead of on 

44 each request. 

45 

46 *Attributes* 

47 

48 schema 

49 The schema node associated with this field. 

50 

51 widget 

52 The widget associated with this field. When no widget is 

53 defined in the schema node, a default widget will be created. 

54 The default widget will have a generated item_css_class 

55 containing the normalized version of the ``name`` attribute 

56 (with ``item`` prepended, e.g. ``item-username``). 

57 NOTE: This behaviour is deprecated and will be removed in 

58 the future. Mapping and Sequence Widget templates simply 

59 render a css class on an item's container based on Field 

60 information. 

61 

62 order 

63 An integer indicating the relative order of this field's 

64 construction to its children and parents. 

65 

66 oid 

67 A string incorporating the ``order`` attribute that can be 

68 used as a unique identifier in HTML code (often for ``id`` 

69 attributes of field-related elements). A default oid is 

70 generated that looks like this: ``deformField0``. A 

71 custom oid can provided, but if the field is cloned, 

72 the clones will get unique default oids. 

73 

74 name 

75 An alias for self.schema.name 

76 

77 title 

78 An alias for self.schema.title 

79 

80 description 

81 An alias for self.schema.description 

82 

83 required 

84 An alias for self.schema.required 

85 

86 typ 

87 An alias for self.schema.typ 

88 

89 children 

90 Child fields of this field. 

91 

92 parent 

93 The parent field of this field or ``None`` if this field is 

94 the root. This is actually a property that returns the result 

95 of ``weakref.ref(actualparent)()`` to avoid leaks due to circular 

96 references, but it can be treated like the field itself. 

97 

98 error 

99 The exception raised by the last attempted validation of the 

100 schema element associated with this field. By default, this 

101 attribute is ``None``. If non-None, this attribute is usually 

102 an instance of the exception class 

103 :exc:`colander.Invalid`, which has a ``msg`` attribute 

104 providing a human-readable validation error message. 

105 

106 errormsg 

107 The ``msg`` attribute of the ``error`` attached to this field 

108 or ``None`` if the ``error`` attached to this field is ``None``. 

109 

110 renderer 

111 The template :term:`renderer` associated with the form. If a 

112 renderer is not passed to the constructor, the default deform 

113 renderer will be used (the :term:`default renderer`). 

114 

115 counter 

116 ``None`` or an instance of ``itertools.counter`` which is used 

117 to generate sequential order-related attributes such as 

118 ``oid`` and ``order``. 

119 

120 resource_registry 

121 The :term:`resource registry` associated with this field. 

122 

123 autofocus 

124 If the field's parent form has its ``focus`` argument set to 

125 ``on``, the first field out of all fields in this form with 

126 ``autofocus`` set to a true-ish value (``on``, ``True``, or 

127 ``autofocus``) will receive focus on page load. Default: ``None``. 

128 

129 *Constructor Arguments* 

130 

131 ``renderer``, ``counter``, ``resource_registry`` and ``appstruct`` are 

132 accepted as explicit keyword arguments to the :class:`deform.Field`. 

133 These are also available as attribute values. ``renderer``, if passed, 

134 is a template renderer as described in :ref:`creating_a_renderer`. 

135 ``counter``, if passed, should be an :attr:`itertools.counter` object 

136 (useful when rendering multiple forms on the same page, see 

137 https://deformdemo.pylonsproject.org/multiple_forms/. 

138 ``resource_registry``, if passed should be a widget resource registry 

139 (see also :ref:`get_widget_resources`). 

140 

141 If any of these values is not passed, a suitable default values is used 

142 in its place. 

143 

144 The ``appstruct`` constructor argument is used to prepopulate field 

145 values related to this form's schema. If an appstruct is not supplied, 

146 the form's fields will be rendered with default values unless an 

147 appstruct is supplied to the ``render`` method explicitly. 

148 

149 The :class:`deform.Field` constructor also accepts *arbitrary* 

150 keyword arguments. When an 'unknown' keyword argument is 

151 passed, it is attached unmodified to the form field as an 

152 attribute. 

153 

154 All keyword arguments (explicit and unknown) are also attached to 

155 all *children* nodes of the field being constructed. 

156 

157 """ 

158 

159 error = None 

160 _cstruct = colander.null 

161 default_renderer = template.default_renderer 

162 default_resource_registry = widget.default_resource_registry 

163 # Allowable input types for automatic focusing 

164 focusable_input_types = ( 

165 type(colander.Boolean()), 

166 type(colander.Date()), 

167 type(colander.DateTime()), 

168 type(colander.Decimal()), 

169 type(colander.Float()), 

170 type(colander.Integer()), 

171 type(colander.Set()), 

172 type(colander.String()), 

173 type(colander.Time()), 

174 ) 

175 hidden_type = type(HiddenWidget()) 

176 

177 def __init__( 

178 self, 

179 schema, 

180 renderer=None, 

181 counter=None, 

182 resource_registry=None, 

183 appstruct=colander.null, 

184 parent=None, 

185 autofocus=None, 

186 **kw 

187 ): 

188 self.counter = counter or itertools.count() 

189 self.order = next(self.counter) 

190 self.oid = getattr(schema, "oid", "deformField%s" % self.order) 

191 self.schema = schema 

192 self.typ = schema.typ # required by Invalid exception 

193 self.name = schema.name 

194 self.title = schema.title 

195 self.description = schema.description 

196 self.required = schema.required 

197 if renderer is None: 

198 renderer = self.default_renderer 

199 if resource_registry is None: 

200 resource_registry = self.default_resource_registry 

201 self.renderer = renderer 

202 

203 # Parameters passed from parent field to child 

204 if "focus" in kw: 

205 focus = kw["focus"] 

206 else: 

207 focus = "on" 

208 if "have_first_input" in kw: 

209 self.have_first_input = kw["have_first_input"] 

210 else: 

211 self.have_first_input = False 

212 

213 if ( 

214 focus == "off" 

215 or autofocus is None 

216 or autofocus is False 

217 or str(autofocus).lower() == "off" 

218 ): 

219 self.autofocus = None 

220 else: 

221 self.autofocus = "autofocus" 

222 

223 self.resource_registry = resource_registry 

224 self.children = [] 

225 if parent is not None: 

226 parent = weakref.ref(parent) 

227 self._parent = parent 

228 self.__dict__.update(kw) 

229 

230 first_input_index = -1 

231 child_count = 0 

232 focused = False 

233 for child in schema.children: 

234 if ( 

235 focus == "on" 

236 and not focused 

237 and type(child.typ) in Field.focusable_input_types 

238 and type(child.widget) != Field.hidden_type 

239 and not self.have_first_input 

240 ): 

241 first_input_index = child_count 

242 self.found_first() # Notify ancestors 

243 autofocus = getattr(child, "autofocus", None) 

244 

245 if autofocus is not None: 

246 focused = True 

247 

248 kw["have_first_input"] = self.have_first_input 

249 self.children.append( 

250 Field( 

251 child, 

252 renderer=renderer, 

253 counter=self.counter, 

254 resource_registry=resource_registry, 

255 parent=self, 

256 autofocus=autofocus, 

257 **kw 

258 ) 

259 ) 

260 child_count += 1 

261 if ( 

262 focus == "on" 

263 and not focused 

264 and first_input_index != -1 

265 and self.have_first_input 

266 ): 

267 # User did not set autofocus. Focus on first valid input. 

268 self.children[first_input_index].autofocus = "autofocus" 

269 self.set_appstruct(appstruct) 

270 

271 def found_first(self): 

272 """ Set have_first_input of ancestors """ 

273 self.have_first_input = True 

274 if self.parent is not None: 

275 self.parent.found_first() 

276 

277 @property 

278 def parent(self): 

279 if self._parent is None: 

280 return None 

281 return self._parent() 

282 

283 def get_root(self): 

284 """ Return the root field in the field hierarchy (the form field) """ 

285 node = self 

286 while True: 

287 parent = node.parent 

288 if parent is None: 

289 break 

290 node = parent 

291 return node 

292 

293 @classmethod 

294 def set_zpt_renderer( 

295 cls, 

296 search_path, 

297 auto_reload=True, 

298 debug=True, 

299 encoding="utf-8", 

300 translator=None, 

301 ): 

302 """Create a :term:`Chameleon` ZPT renderer that will act as a 

303 :term:`default renderer` for instances of the associated class 

304 when no ``renderer`` argument is provided to the class' 

305 constructor. The arguments to this classmethod have the same 

306 meaning as the arguments provided to a 

307 :class:`deform.ZPTRendererFactory`. 

308 

309 Calling this method resets the :term:`default renderer`. 

310 

311 This method is effectively a shortcut for 

312 ``cls.set_default_renderer(ZPTRendererFactory(...))``.""" 

313 cls.default_renderer = template.ZPTRendererFactory( 

314 search_path, 

315 auto_reload=auto_reload, 

316 debug=debug, 

317 encoding=encoding, 

318 translator=translator, 

319 ) 

320 

321 @classmethod 

322 def set_default_renderer(cls, renderer): 

323 """Set the callable that will act as a default renderer for 

324 instances of the associated class when no ``renderer`` 

325 argument is provided to the class' constructor. Useful when 

326 you'd like to use an alternate templating system. 

327 

328 Calling this method resets the :term:`default renderer`. 

329 """ 

330 cls.default_renderer = staticmethod(renderer) 

331 

332 @classmethod 

333 def set_default_resource_registry(cls, registry): 

334 

335 """Set the callable that will act as a default 

336 :term:`resource registry` for instances of the associated 

337 class when no ``resource_registry`` argument is provided to 

338 the class' constructor. Useful when you'd like to use 

339 non-default requirement to resource path mappings for the 

340 entirety of a process. 

341 

342 Calling this method resets the default :term:`resource registry`. 

343 """ 

344 cls.default_resource_registry = registry 

345 

346 def translate(self, msgid): 

347 """Use the translator passed to the renderer of this field to 

348 translate the msgid into a term and return the term. If the renderer 

349 does not have a translator, this method will return the msgid.""" 

350 translate = getattr(self.renderer, "translate", None) 

351 if translate is not None: 

352 return translate(msgid) 

353 return msgid 

354 

355 def __iter__(self): 

356 """ Iterate over the children fields of this field. """ 

357 return iter(self.children) 

358 

359 def __getitem__(self, name): 

360 """Return the subfield of this field named ``name`` or raise 

361 a :exc:`KeyError` if a subfield does not exist named ``name``.""" 

362 for child in self.children: 

363 if child.name == name: 

364 return child 

365 raise KeyError(name) 

366 

367 def __contains__(self, name): 

368 for child in self.children: 

369 if child.name == name: 

370 return True 

371 return False 

372 

373 def clone(self): 

374 """Clone the field and its subfields, retaining attribute 

375 information. Return the cloned field. The ``order`` 

376 attribute of the node is not cloned; instead the field 

377 receives a new order attribute; it will be a number larger 

378 than the last rendered field of this set. The parent of the cloned 

379 node will become ``None`` unconditionally.""" 

380 cloned = self.__class__(self.schema) 

381 cloned.__dict__.update(self.__dict__) 

382 cloned.order = next(cloned.counter) 

383 cloned.oid = "deformField%s" % cloned.order 

384 cloned._parent = None 

385 children = [] 

386 for field in self.children: 

387 cloned_child = field.clone() 

388 cloned_child._parent = weakref.ref(cloned) 

389 children.append(cloned_child) 

390 cloned.children = children 

391 return cloned 

392 

393 @decorator.reify 

394 def widget(self): 

395 """If a widget is not assigned directly to a field, this 

396 function will be called to generate a default widget (only 

397 once). The result of this function will then be assigned as 

398 the ``widget`` attribute of the field for the rest of the 

399 lifetime of this field. If a widget is assigned to a field 

400 before form processing, this function will not be called.""" 

401 wdg = getattr(self.schema, "widget", None) 

402 if wdg is not None: 

403 return wdg 

404 widget_maker = getattr(self.schema.typ, "widget_maker", None) 

405 if widget_maker is None: 

406 widget_maker = schema.default_widget_makers.get( 

407 self.schema.typ.__class__ 

408 ) 

409 if widget_maker is None: 

410 for (cls, wgt) in schema.default_widget_makers.items(): 

411 if isinstance(self.schema.typ, cls): 

412 widget_maker = wgt 

413 break 

414 if widget_maker is None: 

415 widget_maker = widget.TextInputWidget 

416 return widget_maker(item_css_class=self.default_item_css_class()) 

417 

418 def default_item_css_class(self): 

419 if not self.name: 

420 return None 

421 

422 css_class = ( 

423 unicodedata.normalize("NFKD", compat.text_type(self.name)) 

424 .encode("ascii", "ignore") 

425 .decode("ascii") 

426 ) 

427 css_class = re.sub(r"[^\w\s-]", "", css_class).strip().lower() # noQA 

428 css_class = re.sub(r"[-\s]+", "-", css_class) # noQA 

429 return "item-%s" % css_class 

430 

431 def get_widget_requirements(self): 

432 """Return a sequence of two tuples in the form 

433 [(``requirement_name``, ``version``), ..]. 

434 

435 The first element in each two-tuple represents a requirement 

436 name. When a requirement name is returned as part of 

437 ``get_widget_requirements``, it means that one or more CSS or 

438 Javascript resources need to be loaded by the page performing 

439 the form rendering in order for some widget on the page to 

440 function properly. 

441 

442 The second element in each two-tuple is the requested version 

443 of the library resource. It may be ``None``, in which case 

444 the version is unspecified. 

445 

446 See also the ``requirements`` attribute of 

447 :class:`deform.Widget` and the explanation of widget 

448 requirements in :ref:`get_widget_requirements`. 

449 """ 

450 L = [] 

451 

452 requirements = [req for req in self.widget.requirements] + [ 

453 req 

454 for child in self.children 

455 for req in child.get_widget_requirements() 

456 ] 

457 

458 if requirements: 

459 for requirement in requirements: 

460 if isinstance(requirement, dict): 

461 L.append(requirement) 

462 else: 

463 reqt = tuple(requirement) 

464 if reqt not in L: 

465 L.append(reqt) 

466 return L 

467 

468 def get_widget_resources(self, requirements=None): 

469 """Return a resources dictionary in the form ``{'js':[seq], 

470 'css':[seq]}``. ``js`` represents Javascript resources, 

471 ``css`` represents CSS resources. ``seq`` represents a 

472 sequence of resource paths. Each path in ``seq`` represents a 

473 relative resource name, as defined by the mapping of a 

474 requirement to a set of resource specification by the 

475 :term:`resource registry` attached to this field or form. 

476 

477 This method may raise a :exc:`ValueError` if the resource 

478 registry associated with this field or form cannot resolve a 

479 requirement to a set of resource paths. 

480 

481 The ``requirements`` argument represents a set of requirements 

482 as returned by a manual call to 

483 :meth:`deform.Field.get_widget_requirements`. If 

484 ``requirements`` is not supplied, the requirement are implied 

485 by calling the :meth:`deform.Field.get_widget_requirements` 

486 method against this form field. 

487 

488 See also :ref:`get_widget_resources`. 

489 """ 

490 if requirements is None: 

491 requirements = self.get_widget_requirements() 

492 resources = self.resource_registry( 

493 (req for req in requirements if not isinstance(req, dict)) 

494 ) 

495 for req in requirements: 

496 if not isinstance(req, dict): 

497 continue 

498 for key in {'js', 'css'}.intersection(req): 

499 value = req[key] 

500 if isinstance(value, str): 

501 resources[key].append(value) 

502 else: 

503 resources[key].extend(value) 

504 return resources 

505 

506 def set_widgets(self, values, separator="."): 

507 """set widgets of the child fields of this field 

508 or form element. ``widgets`` should be a dictionary in the 

509 form:: 

510 

511 {'dotted.field.name':Widget(), 

512 'dotted.field.name2':Widget()} 

513 

514 The keys of the dictionary are dotted names. Each dotted name 

515 refers to a single field in the tree of fields that are 

516 children of the field or form object upon which this method is 

517 called. 

518 

519 The dotted name is split on its dots and the resulting list of 

520 names is used as a search path into the child fields of this 

521 field in order to find a field to which to assign the 

522 associated widget. 

523 

524 Two special cases exist: 

525 

526 - If the key is the empty string (``''``), the widget is 

527 assigned to the field upon which this method is called. 

528 

529 - If the key contains an asterisk as an element name, the 

530 first child of the found element is traversed. This is most 

531 useful for sequence fields, because the first (and only) 

532 child of sequence fields is always the prototype field which 

533 is used to render all fields in the sequence within a form 

534 rendering. 

535 

536 If the ``separator`` argument is passed, it is should be a 

537 string to be used as the dot character when splitting the 

538 dotted names (useful for supplying if one of your field object 

539 has a dot in its name, and you need to use a different 

540 separator). 

541 

542 Examples follow. If the following form is used:: 

543 

544 class Person(Schema): 

545 first_name = SchemaNode(String()) 

546 last_name = SchemaNode(String()) 

547 

548 class People(SequenceSchema): 

549 person = Person() 

550 

551 class Conference(Schema): 

552 people = People() 

553 name = SchemaNode(String()) 

554 

555 schema = Conference() 

556 form = Form(schema) 

557 

558 The following invocations will have the following results 

559 against the schema defined above: 

560 

561 ``form.set_widgets({'people.person.first_name':TextAreaWidget()})`` 

562 

563 Set the ``first_name`` field's widget to a ``TextAreaWidget``. 

564 

565 ``form.set_widgets({'people.*.first_name':TextAreaWidget()})`` 

566 

567 Set the ``first_name`` field's widget to a 

568 ``TextAreaWidget``. 

569 

570 ``form.set_widgets({'people':MySequenceWidget()})`` 

571 

572 Set the ``people`` sequence field's widget to a 

573 ``MySequenceWidget``. 

574 

575 ``form.set_widgets({'people.*':MySequenceWidget()})`` 

576 

577 Set the *person* field's widget to a ``MySequenceWidget``. 

578 

579 ``form.set_widgets({'':MyMappingWidget()})`` 

580 

581 Set *form* node's widget to a ``MyMappingWidget``. 

582 

583 """ 

584 for k, v in values.items(): 

585 if not k: 

586 self.widget = v 

587 else: 

588 path = k.split(separator) 

589 field = self 

590 while path: 

591 element = path.pop(0) 

592 if element == "*": 

593 field = field.children[0] 

594 else: 

595 field = field[element] 

596 field.widget = v 

597 

598 @property 

599 def errormsg(self): 

600 """Return the ``msg`` attribute of the ``error`` attached to 

601 this field. If the ``error`` attribute is ``None``, 

602 the return value will be ``None``.""" 

603 return getattr(self.error, "msg", None) 

604 

605 def serialize(self, cstruct=_marker, **kw): 

606 """Serialize the cstruct into HTML and return the HTML string. This 

607 function just turns around and calls ``self.widget.serialize(**kw)``; 

608 therefore the field widget's ``serialize`` method should be expecting 

609 any values sent in ``kw``. If ``cstruct`` is not passed, the cstruct 

610 attached to this node will be injected into ``kw`` as ``cstruct``. 

611 If ``field`` is not passed in ``kw``, this field will be injected 

612 into ``kw`` as ``field``. 

613 

614 .. note:: 

615 

616 Deform versions before 0.9.8 only accepted a ``readonly`` 

617 keyword argument to this function. Version 0.9.8 and later accept 

618 arbitrary keyword arguments. It also required that 

619 ``cstruct`` was passed; it's broken out from 

620 ``kw`` in the method signature for backwards compatibility. 

621 """ 

622 if cstruct is _marker: 

623 cstruct = self.cstruct 

624 values = {"field": self, "cstruct": cstruct} 

625 values.update(kw) 

626 return self.widget.serialize(**values) 

627 

628 def deserialize(self, pstruct): 

629 """ Deserialize the pstruct into a cstruct and return the cstruct.""" 

630 return self.widget.deserialize(self, pstruct) 

631 

632 def render(self, appstruct=_marker, **kw): 

633 """Render the field (or form) to HTML using ``appstruct`` as a set 

634 of default values and returns the HTML string. ``appstruct`` is 

635 typically a dictionary of application values matching the schema used 

636 by this form, or ``colander.null`` to render all defaults. If it 

637 is omitted, the rendering will use the ``appstruct`` passed to the 

638 constructor. 

639 

640 Calling this method passing an appstruct is the same as calling:: 

641 

642 cstruct = form.set_appstruct(appstruct) 

643 form.serialize(cstruct, **kw) 

644 

645 Calling this method without passing an appstruct is the same as 

646 calling:: 

647 

648 cstruct = form.cstruct 

649 form.serialize(cstruct, **kw) 

650 

651 See the documentation for 

652 :meth:`colander.SchemaNode.serialize` and 

653 :meth:`deform.widget.Widget.serialize` . 

654 

655 .. note:: 

656 

657 Deform versions before 0.9.8 only accepted a ``readonly`` 

658 keyword argument to this function. Version 0.9.8 and later accept 

659 arbitrary keyword arguments. 

660 """ 

661 if appstruct is not _marker: 

662 self.set_appstruct(appstruct) 

663 cstruct = self.cstruct 

664 kw.pop("cstruct", None) # disallowed 

665 html = self.serialize(cstruct, **kw) 

666 return html 

667 

668 def validate(self, controls, subcontrol=None): 

669 """ 

670 Validate the set of controls returned by a form submission 

671 against the schema associated with this field or form. 

672 ``controls`` should be a *document-ordered* sequence of 

673 two-tuples that represent the form submission data. Each 

674 two-tuple should be in the form ``(key, value)``. ``node`` 

675 should be the schema node associated with this widget. 

676 

677 For example, using WebOb, you can compute a suitable value for 

678 ``controls`` via:: 

679 

680 request.POST.items() 

681 

682 Or, if you're using a ``cgi.FieldStorage`` object named 

683 ``fs``, you can compute a suitable value for ``controls`` 

684 via:: 

685 

686 controls = [] 

687 if fs.list: 

688 for control in fs.list: 

689 if control.filename: 

690 controls.append((control.name, control)) 

691 else: 

692 controls.append((control.name, control.value)) 

693 

694 Equivalent ways of computing ``controls`` should be available to 

695 any web framework. 

696 

697 When the ``validate`` method is called: 

698 

699 - if the fields are successfully validated, a data structure 

700 represented by the deserialization of the data as per the 

701 schema is returned. It will be a mapping. 

702 

703 - If the fields cannot be successfully validated, a 

704 :exc:`deform.exception.ValidationFailure` exception is raised. 

705 

706 The typical usage of ``validate`` in the wild is often 

707 something like this (at least in terms of code found within 

708 the body of a :mod:`pyramid` view function, the particulars 

709 will differ in your web framework):: 

710 

711 from webob.exc import HTTPFound 

712 from deform.exception import ValidationFailure 

713 from deform import Form 

714 import colander 

715 

716 from my_application import do_something 

717 

718 class MySchema(colander.MappingSchema): 

719 color = colander.SchemaNode(colander.String()) 

720 

721 schema = MySchema() 

722 

723 def view(request): 

724 form = Form(schema, buttons=('submit',)) 

725 if 'submit' in request.POST: # form submission needs validation 

726 controls = request.POST.items() 

727 try: 

728 deserialized = form.validate(controls) 

729 do_something(deserialized) 

730 return HTTPFound(location='http://example.com/success') 

731 except ValidationFailure as e: 

732 return {'form':e.render()} 

733 else: 

734 return {'form':form.render()} # the form just needs rendering 

735 

736 .. warning:: 

737 

738 ``form.validate(controls)`` mutates the ``form`` instance, so the 

739 ``form`` instance should be constructed (and live) inside one 

740 request. 

741 

742 If ``subcontrol`` is supplied, it represents a named subitem in the 

743 data returned by ``peppercorn.parse(controls)``. Use this subitem as 

744 the pstruct to validate instead of using the entire result of 

745 ``peppercorn.parse(controls)`` as the pstruct to validate. For 

746 example, if you've embedded a mapping in the form named ``user``, and 

747 you want to validate only the data contained in that mapping instead 

748 if all of the data in the form post, you might use 

749 ``form.validate(controls, subcontrol='user')``. 

750 """ 

751 try: 

752 pstruct = peppercorn.parse(controls) 

753 except ValueError as e: 

754 exc = colander.Invalid( 

755 self.schema, "Invalid peppercorn controls: %s" % e 

756 ) 

757 self.widget.handle_error(self, exc) 

758 cstruct = colander.null 

759 raise exception.ValidationFailure(self, cstruct, exc) 

760 if subcontrol is not None: 

761 pstruct = pstruct.get(subcontrol, colander.null) 

762 return self.validate_pstruct(pstruct) 

763 

764 def validate_pstruct(self, pstruct): 

765 """ 

766 Validate the pstruct passed. Works exactly like the 

767 :class:`deform.field.validate` method, except it accepts a pstruct 

768 instead of a set of form controls. A usage example follows:: 

769 

770 if 'submit' in request.POST: # the form submission needs validation 

771 controls = request.POST.items() 

772 pstruct = peppercorn.parse(controls) 

773 substruct = pstruct['submapping'] 

774 try: 

775 deserialized = form.validate_pstruct(substruct) 

776 do_something(deserialized) 

777 return HTTPFound(location='http://example.com/success') 

778 except ValidationFailure, e: 

779 return {'form':e.render()} 

780 else: 

781 return {'form':form.render()} # the form just needs rendering 

782 """ 

783 

784 exc = None 

785 

786 try: 

787 cstruct = self.deserialize(pstruct) 

788 except colander.Invalid as e: 

789 # fill in errors raised by widgets 

790 self.widget.handle_error(self, e) 

791 cstruct = e.value 

792 exc = e 

793 

794 self.cstruct = cstruct 

795 

796 try: 

797 appstruct = self.schema.deserialize(cstruct) 

798 except colander.Invalid as e: 

799 # fill in errors raised by schema nodes 

800 self.widget.handle_error(self, e) 

801 exc = e 

802 

803 if exc: 

804 raise exception.ValidationFailure(self, cstruct, exc) 

805 

806 return appstruct 

807 

808 def _get_cstruct(self): 

809 return self._cstruct 

810 

811 def _set_cstruct(self, cstruct): 

812 self._cstruct = cstruct 

813 child_cstructs = self.schema.cstruct_children(cstruct) 

814 if not isinstance(child_cstructs, colander.SequenceItems): 

815 # If the schema's type returns SequenceItems, it means that the 

816 # node is a sequence node, which means it has one child 

817 # representing its prototype instead of a set of "real" children; 

818 # our widget handle cloning the prototype node. The prototype's 

819 # cstruct will already be set up with its default value by virtue 

820 # of set_appstruct having been called in its constructor, and we 

821 # needn't (and can't) do anything more. 

822 for n, child in enumerate(self.children): 

823 child.cstruct = child_cstructs[n] 

824 

825 def _del_cstruct(self): 

826 if "_cstruct" in self.__dict__: 

827 # rely on class-scope _cstruct (null) 

828 del self._cstruct 

829 

830 cstruct = property(_get_cstruct, _set_cstruct, _del_cstruct) 

831 

832 def __repr__(self): 

833 return "<%s.%s object at %d (schemanode %r)>" % ( 

834 self.__module__, 

835 self.__class__.__name__, 

836 id(self), 

837 self.schema.name, 

838 ) 

839 

840 def set_appstruct(self, appstruct): 

841 """Set the cstruct of this node (and its child nodes) using 

842 ``appstruct`` as input.""" 

843 cstruct = self.schema.serialize(appstruct) 

844 self.cstruct = cstruct 

845 return cstruct 

846 

847 def set_pstruct(self, pstruct): 

848 """Set the cstruct of this node (and its child nodes) using 

849 ``pstruct`` as input.""" 

850 try: 

851 cstruct = self.deserialize(pstruct) 

852 except colander.Invalid as e: 

853 # explicitly don't set errors 

854 cstruct = e.value 

855 self.cstruct = cstruct 

856 

857 def render_template(self, template, **kw): 

858 """Render the template named ``template`` using ``kw`` as the 

859 top-level keyword arguments (augmented with ``field`` and ``cstruct`` 

860 if necessary)""" 

861 values = {"field": self, "cstruct": self.cstruct} 

862 values.update(kw) # allow caller to override field and cstruct 

863 return self.renderer(template, **values) 

864 

865 # retail API 

866 

867 def start_mapping(self, name=None): 

868 """Create a start-mapping tag (a literal). If ``name`` is ``None``, 

869 the name of this node will be used to generate the name in the tag. 

870 See the :term:`Peppercorn` documentation for more information. 

871 """ 

872 if name is None: 

873 name = self.name 

874 tag = '<input type="hidden" name="__start__" value="%s:mapping"/>' 

875 return Markup(tag % (name,)) 

876 

877 def end_mapping(self, name=None): 

878 """Create an end-mapping tag (a literal). If ``name`` is ``None``, 

879 the name of this node will be used to generate the name in the tag. 

880 See the :term:`Peppercorn` documentation for more information. 

881 """ 

882 if name is None: 

883 name = self.name 

884 tag = '<input type="hidden" name="__end__" value="%s:mapping"/>' 

885 return Markup(tag % (name,)) 

886 

887 def start_sequence(self, name=None): 

888 """Create a start-sequence tag (a literal). If ``name`` is ``None``, 

889 the name of this node will be used to generate the name in the tag. 

890 See the :term:`Peppercorn` documentation for more information. 

891 """ 

892 if name is None: 

893 name = self.name 

894 tag = '<input type="hidden" name="__start__" value="%s:sequence"/>' 

895 return Markup(tag % (name,)) 

896 

897 def end_sequence(self, name=None): 

898 """Create an end-sequence tag (a literal). If ``name`` is ``None``, 

899 the name of this node will be used to generate the name in the tag. 

900 See the :term:`Peppercorn` documentation for more information. 

901 """ 

902 

903 if name is None: 

904 name = self.name 

905 tag = '<input type="hidden" name="__end__" value="%s:sequence"/>' 

906 return Markup(tag % (name,)) 

907 

908 def start_rename(self, name=None): 

909 """Create a start-rename tag (a literal). If ``name`` is ``None``, 

910 the name of this node will be used to generate the name in the tag. 

911 See the :term:`Peppercorn` documentation for more information. 

912 """ 

913 if name is None: 

914 name = self.name 

915 tag = '<input type="hidden" name="__start__" value="%s:rename"/>' 

916 return Markup(tag % (name,)) 

917 

918 def end_rename(self, name=None): 

919 """Create a start-rename tag (a literal). If ``name`` is ``None``, 

920 the name of this node will be used to generate the name in the tag. 

921 See the :term:`Peppercorn` documentation for more information. 

922 """ 

923 if name is None: 

924 name = self.name 

925 tag = '<input type="hidden" name="__end__" value="%s:rename"/>' 

926 return Markup(tag % (name,))