为zope.formlib提供的额外浏览器小工具和替代方法。
项目描述
zc.form包是一个可能暂时附加的组件,用于存放额外的浏览器小工具和对zope.formlib包中代码的替代方法。大部分或所有代码都是由Zope公司创建的,目的是最终将其集成到Zope 3的主要版本中。
变更
2.0 (2023-02-06)
添加对Python 3.8、3.9、3.10、3.11的支持。
停止支持Python 2.7、3.5、3.6。
1.1 (2019-02-11)
如果未安装[mruwidget]额外组件,则修复ZCML配置问题。
1.0 (2019-01-11)
功能
声明支持Python 3.5、3.6、3.7、PyPy和PyPy3。
错误修复
修复BaseVocabularyDisplay.render()中的NameError。
实际上将Combination字段上的missing_value集合传递给包含字段。
注意事项
安装MruSourceInputWidget和TimeZoneWidget需要[mruwidget]额外组件以打破对zc.resourcelibrary的依赖,对于不需要该组件的项目。
0.5 (2016-08-02)
绑定包含在zc.form.field.Combination中的字段以修复这些字段的上下文。
0.4 (2016-01-12)
移除对zope.app.pagetemplate的依赖。
0.3 (2014-04-23)
移除zc.form.field.Combination至少需要两个子字段的要求。
0.2 (2011-09-24)
通过要求至少zope.formlib 4.0来移除对zope.app.form的依赖。
通过要求至少zope.component 3.8来移除对zope.app.component的依赖。
依赖于zope.catalog而不是zope.app.catalog。
依赖于zope.security而不是zope.app.security。
在测试设置中,依赖于zope.app.wsgi >= 3.7而不是zope.app.testing。
依赖于zope.browserpage和zope.container而不是zope.app.publisher。
移除了以下依赖
zope.app.basicskin
zope.app.securitypolicy
zope.app.zapi
zope.app.zcmlfiles
修复测试以在zope.schema >= 3.6下运行。
使包适合在ZTK 1.1上运行。
将测试依赖项移动到test额外组件。
使用Python的doctest模块而不是已弃用的zope.testing.doctest。
0.1
异常视图现在支持unicode。以前它们会在翻译内容上崩溃。
向Union字段添加use_default_for_not_selected以使用默认值,即使子字段未选择。
组合控件
组合控件收集两个或多个子字段,以便以方便的方式指定一系列值。
渲染控件返回一个包含子字段的表格。
>>> from zc.form.browser.combinationwidget import (
... CombinationWidget, CombinationDisplayWidget, default_template)
>>> from zope import component, interface
>>> component.provideAdapter(default_template, name='default')
>>> from zc.form.field import Combination, OrderedCombinationConstraint
>>> from zope.schema import Int
>>> from zope.schema.interfaces import IInt
>>> from zope.publisher.interfaces.browser import IBrowserRequest
>>> from zope.formlib.interfaces import IInputWidget
>>> from zope.formlib.textwidgets import IntWidget
>>> component.provideAdapter(
... IntWidget, (IInt, IBrowserRequest), IInputWidget)
>>> from zope import interface
>>> class IDemo(interface.Interface):
... acceptable_count = Combination(
... (Int(title=u'Minimum', required=True, min=0),
... Int(title=u'Maximum', required=False)),
... title=u'Acceptable Count',
... required=False,
... constraints=(OrderedCombinationConstraint(),))
...
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> widget = CombinationWidget(IDemo['acceptable_count'], request)
>>> widget.setPrefix('field')
>>> widget.loadValueFromRequest() # None
>>> print(widget())
<input type='hidden' name='field.acceptable_count-marker' value='x' />
<table class="combinationFieldWidget">
<tr>
<td class="label">
<label for="field.acceptable_count.combination_00">
<span class="required">*</span><span>Minimum</span>
</label>
</td>
<td class="field">
<div class="widget"><input class="textType"
id="field.acceptable_count.combination_00"
name="field.acceptable_count.combination_00" size="10" type="text"
value="" />
</div>
</td>
</tr>
<tr>
<td class="label">
<label for="field.acceptable_count.combination_01">
<span>Maximum</span>
</label>
</td>
<td class="field">
<div class="widget"><input class="textType"
id="field.acceptable_count.combination_01"
name="field.acceptable_count.combination_01" size="10" type="text"
value="" />
</div>
</td>
</tr>
</table>
在请求中设置适当的值,让控件正确读取指定的值。
>>> request.form['field.acceptable_count-marker'] = 'x'
>>> request.form['field.acceptable_count.combination_00'] = '10'
>>> request.form['field.acceptable_count.combination_01'] = ''
>>> widget = CombinationWidget(IDemo['acceptable_count'], request)
>>> widget.setPrefix('field')
>>> widget.getInputValue()
(10, None)
>>> print(widget())
<...
...<input class="textType" id="field.acceptable_count.combination_00"
name="field.acceptable_count.combination_00" size="10" type="text"
value="10" />...
...<input class="textType" id="field.acceptable_count.combination_01"
name="field.acceptable_count.combination_01" size="10" type="text"
value="" />...
该字段对空值没有问题,因为它不是必需的。
>>> request.form['field.acceptable_count-marker'] = 'x'
>>> request.form['field.acceptable_count.combination_00'] = ''
>>> request.form['field.acceptable_count.combination_01'] = ''
>>> widget = CombinationWidget(IDemo['acceptable_count'], request)
>>> widget.setPrefix('field')
>>> widget.getInputValue() # None
>>> print(widget())
<...
...<input class="textType" id="field.acceptable_count.combination_00"
name="field.acceptable_count.combination_00" size="10" type="text"
value="" />...
...<input class="textType" id="field.acceptable_count.combination_01"
name="field.acceptable_count.combination_01" size="10" type="text"
value="" />...
>>> bool(widget.error())
False
>>> bool(widget.widgets[0].error())
False
但是,如果可选值被填写,而必需的值没有填写,则存在错误。
>>> request.form['field.acceptable_count-marker'] = 'x'
>>> request.form['field.acceptable_count.combination_00'] = ''
>>> request.form['field.acceptable_count.combination_01'] = '10'
>>> widget = CombinationWidget(IDemo['acceptable_count'], request)
>>> widget.setPrefix('field')
>>> widget.getInputValue()
Traceback (most recent call last):
WidgetInputError: ('acceptable_count', u'Acceptable Count',
WidgetInputError('combination_00', u'Minimum',
RequiredMissing('combination_00')))
>>> import zope.formlib.interfaces
>>> import zope.publisher.interfaces.browser
>>> @interface.implementer(zope.formlib.interfaces.IWidgetInputErrorView)
... @component.adapter(zope.formlib.interfaces.WidgetInputError,
... zope.publisher.interfaces.browser.IBrowserRequest)
... class SnippetView(object):
...
... def __init__(self, context, request):
... self.context = context
... self.request = request
... def snippet(self):
... return self.context.doc()
...
>>> component.provideAdapter(SnippetView)
>>> print(widget())
<...
...<input class="textType" id="field.acceptable_count.combination_00"
name="field.acceptable_count.combination_00" size="10"
type="text" value="" />...
...Required input is missing...
...<input class="textType" id="field.acceptable_count.combination_01"
name="field.acceptable_count.combination_01" size="10"
type="text" value="10" />...
>>> print(widget.error())
Required input is missing.
>>> print(widget.widgets[0].error())
Required input is missing.
类似地,如果字段的条件未满足,则控件显示错误。
>>> request.form['field.acceptable_count-marker'] = 'x'
>>> request.form['field.acceptable_count.combination_00'] = '20'
>>> request.form['field.acceptable_count.combination_01'] = '10'
>>> widget = CombinationWidget(IDemo['acceptable_count'], request)
>>> widget.setPrefix('field')
>>> widget.getInputValue()
Traceback (most recent call last):
WidgetInputError: ('acceptable_count', u'Acceptable Count',
MessageValidationError(u'${minimum} ...
>>> print(widget())
<...
...input class="textType" id="field.acceptable_count.combination_00"
name="field.acceptable_count.combination_00" size="10"
type="text" value="20" />...
...<input class="textType" id="field.acceptable_count.combination_01"
name="field.acceptable_count.combination_01" size="10"
type="text" value="10" />...
>>> print(widget.error())
${minimum} must be less than or equal to ${maximum}.
还有一个控件的显示版本。
>>> request = TestRequest()
>>> from zope.formlib.widget import DisplayWidget
>>> from zope.formlib.interfaces import IDisplayWidget
>>> component.provideAdapter(
... DisplayWidget, (IInt, IBrowserRequest), IDisplayWidget)
>>> widget = CombinationDisplayWidget(IDemo['acceptable_count'], request)
>>> widget.setPrefix('field')
>>> widget.setRenderedValue(('10', '2'))
>>> print(widget())
<input type='hidden' name='field.acceptable_count-marker' value='x' />
<table class="combinationFieldWidget">
<tr>
<td class="label">
<label for="field.acceptable_count.combination_00">
<span>Minimum</span>
</label>
</td>
<td class="field">
<div class="widget">10
</div>
</td>
</tr>
<tr>
<td class="label">
<label for="field.acceptable_count.combination_01">
<span>Maximum</span>
</label>
</td>
<td class="field">
<div class="widget">2
</div>
</td>
</tr>
</table>
如果参数数量不正确,则使用missing_value。
>>> field = IDemo['acceptable_count']
>>> field.missing_value=('23', '42')
>>> widget = CombinationDisplayWidget(field, request)
>>> widget.setPrefix('field')
>>> widget.setRenderedValue(('10', '2', '3'))
>>> print(widget())
<input type='hidden' name='field.acceptable_count-marker' value='x' />
<table class="combinationFieldWidget">
<tr>
<td class="label">
<label for="field.acceptable_count.combination_00">
<span>Minimum</span>
</label>
</td>
<td class="field">
<div class="widget">23
</div>
</td>
</tr>
<tr>
<td class="label">
<label for="field.acceptable_count.combination_01">
<span>Maximum</span>
</label>
</td>
<td class="field">
<div class="widget">42
</div>
</td>
</tr>
</table>
如果参数不是序列,则使用missing_value。
>>> widget = CombinationDisplayWidget(field, request)
>>> widget.setPrefix('field')
>>> widget.setRenderedValue(10)
>>> print(widget())
<input type='hidden' name='field.acceptable_count-marker' value='x' />
<table class="combinationFieldWidget">
<tr>
<td class="label">
<label for="field.acceptable_count.combination_00">
<span>Minimum</span>
</label>
</td>
<td class="field">
<div class="widget">23
</div>
</td>
</tr>
<tr>
<td class="label">
<label for="field.acceptable_count.combination_01">
<span>Maximum</span>
</label>
</td>
<td class="field">
<div class="widget">42
</div>
</td>
</tr>
</table>
对于布尔值,标签和字段的顺序颠倒。
>>> request = TestRequest()
>>> from zope.schema import Bool
>>> from zope.schema.interfaces import IBool
>>> from zope.formlib.boolwidgets import CheckBoxWidget
>>> from zope.formlib.widget import DisplayWidget
>>> from zope.formlib.interfaces import IDisplayWidget
>>> component.provideAdapter(
... CheckBoxWidget, (IBool, IBrowserRequest), IInputWidget)
>>> class IBoolDemo(interface.Interface):
... choices = Combination(
... (Bool(title=u'first'),
... Bool(title=u'second')),
... title=u'Choices',
... required=False,)
>>> widget = CombinationWidget(IBoolDemo['choices'], request)
>>> widget.setPrefix('field')
>>> print(widget())
<input type='hidden' name='field.choices-marker' value='x' />
<table class="combinationFieldWidget">
<tr>
<td></td>
<td class="field">
<div class="widget"><input class="hiddenType" id="field.choices.combination_00.used" name="field.choices.combination_00.used" type="hidden" value="" /> <input class="checkboxType" id="field.choices.combination_00" name="field.choices.combination_00" type="checkbox" value="on" />
<span>first</span>
</div>
</td>
</tr>
<tr>
<td></td>
<td class="field">
<div class="widget"><input class="hiddenType" id="field.choices.combination_01.used" name="field.choices.combination_01.used" type="hidden" value="" /> <input class="checkboxType" id="field.choices.combination_01" name="field.choices.combination_01" type="checkbox" value="on" />
<span>second</span>
</div>
</td>
</tr>
</table>
最近使用(MRU)源控件
MRU控件跟踪最后选定的几个值(基于每个主体),并允许快速从该列表中选择,而不是使用查询界面。
我们可以通过使用自定义表单来查看控件的实际操作。让我们定义一个使用源形式的模式
>>> import zope.interface >>> import zope.schema >>> class IDemo(zope.interface.Interface): ... ... color = zope.schema.Choice( ... title=u"Color", ... description=u"My favorite color", ... source=AvailableColors, ... )
然后是一个实现接口的类
>>> @zope.interface.implementer(IDemo) ... class Demo(object): ... ... color = None
我们需要一个使用此模式的表单
>>> import zope.formlib.form >>> class DemoInput(zope.formlib.form.EditForm): ... actions = () ... form_fields = zope.formlib.form.fields(IDemo)
通过渲染表单,我们可以看到没有可选择的MRU项(因为此主体以前从未访问过此表单),并且显示查询界面
>>> import zope.publisher.browser >>> import zope.security.interfaces >>> import zope.security.management >>> import zope.component.hooks >>> @zope.interface.implementer(zope.security.interfaces.IPrincipal) ... class DummyPrincipal(object): ... ... id = "someuser" ... title = "Some User's Name" ... description = "A User"
请注意,我们需要使用特殊的资源库请求。在这里,我们正在组合 TestRequest 和资源库请求;当我们切换到 TestBrowser 时,可以移除这个异常。
>>> import zc.resourcelibrary.publication >>> class TestRequest(zope.publisher.browser.TestRequest, ... zc.resourcelibrary.publication.Request): ... def _createResponse(self): ... return zc.resourcelibrary.publication.Request._createResponse( ... self) ...>>> request = TestRequest() >>> principal = DummyPrincipal() >>> request.setPrincipal(principal) >>> zope.security.management.newInteraction(request)>>> oldsite = zope.component.hooks.getSite() >>> zope.component.hooks.setSite(getRootFolder())
现在我们可以使用我们的演示对象实例来查看表单是否从我们上面定义的词汇表中提取可能的值。
>>> form = DemoInput(Demo(), request)
>>> print(form())
<...
<div class="queries"...>
<div class="query"...>
<div class="queryinput"...>
<query view for colors>
</div> <!-- queryinput -->
</div> <!-- query -->
</div> <!-- queries -->
...
请注意,MRU 值的选项框没有出现在输出中,因为用户之前从未选择过任何值。
>>> '<select name="form.color">' not in form() True
现在,我们可以选择其中一个值。
>>> zope.security.management.endInteraction()
>>> request = TestRequest()
>>> request.form = {
... 'form.color.query.selection': 'red_token',
... 'form.color.query.apply': 'Apply',
... 'form.color.displayed': '',
... }
>>> request.setPrincipal(principal)
>>> zope.security.management.newInteraction(request)
处理请求后,MRU 值列表出现在表单中。
>>> form = DemoInput(Demo(), request) >>> print(form()) <... <select name="form.color" id="form.color"> <option value="red_token" selected="selected">Red</option> </select> ...
并且查询视图被隐藏,因为我们有一个 MRU 列表。
>>> print(form()) <... <input type="hidden" name="form.color.queries.visible" ... value="no"> ...
如果我们选择另一个值...
>>> request = TestRequest()
>>> request.form = {
... 'form.color.query.selection': 'green_token',
... 'form.color.query.apply': 'Apply',
... 'form.color.displayed': '',
... }
>>> request.setPrincipal(principal)
...并处理请求,MRU 值列表包括新的值,位于顶部,并且被选中。
>>> form = DemoInput(Demo(), request) >>> print(form()) <... <select name="form.color" id="form.color"> <option value="green_token" selected="selected">Green</option> <option value="red_token">Red</option> </select> ...
如果我们请求源中不存在的值,一切保持不变,但没有选择任何值。
>>> request = TestRequest()
>>> request.form = {
... 'form.color.query.selection': 'blue_token',
... 'form.color.query.apply': 'Apply',
... 'form.color.displayed': '',
... }
>>> request.setPrincipal(principal)
>>> form = DemoInput(Demo(), request)
>>> print(form())
<...
<select name="form.color" id="form.color">
<option value="green_token">Green</option>
<option value="red_token">Red</option>
</select>
...
我们可以使查询可见。
>>> request = TestRequest()
>>> request.form = {
... 'form.color.query.selection': 'red_token',
... 'form.color.query.apply': 'Apply',
... 'form.color.queries.visible': 'yes',
... 'form.color.query.search': 'yes',
... 'form.color.query.searchstring': 'red',
... 'form.color.displayed': '',
... }
>>> request.setPrincipal(principal)
>>> form = DemoInput(Demo(), request)
>>> print(form())
<...
<select name="form.color" id="form.color">
<option value="red_token" selected="selected">Red</option>
<option value="green_token">Green</option>
</select>
...
<select name="form.color.query.selection">
<option value="red_token">Red</option>
</select>
<input type="submit" name="form.color.query.apply" value="Apply" />
...
如果没有应用查询,则不会显示。
>>> request = TestRequest()
>>> request.form = {
... 'form.color.query.selection': 'red_token',
... 'form.color.queries.visible': 'yes',
... 'form.color.query.search': 'yes',
... 'form.color.query.searchstring': 'red',
... 'form.color.displayed': '',
... }
>>> request.setPrincipal(principal)
>>> form = DemoInput(Demo(), request)
>>> print(form())
<...
<select name="form.color" id="form.color">
<option value="red_token">Red</option>
<option value="green_token">Green</option>
</select>
...
<select name="form.color.query.selection">
<option value="red_token">Red</option>
</select>
<input type="submit" name="form.color.query.apply" value="Apply" />
...
如果标记不在源中,则忽略主要标记中的标记。
>>> from zope.annotation.interfaces import IAnnotations
>>> annotations = IAnnotations(principal)
>>> annotation = annotations.get('zc.form.browser.mruwidget')
>>> tokens = annotation.get('form.color')
>>> tokens.append('black_token')
>>> tokens
['red_token', 'green_token', 'black_token']
>>> print(form())
<...
<select name="form.color" id="form.color">
<option value="red_token">Red</option>
<option value="green_token">Green</option>
</select>
...
<select name="form.color.query.selection">
<option value="red_token">Red</option>
</select>
<input type="submit" name="form.color.query.apply" value="Apply" />
...
稍微整理一下。
>>> zope.security.management.endInteraction() >>> zope.component.hooks.setSite(oldsite)
项目详情
下载文件
下载适用于您的平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。