跳转到主要内容

基于z3c.form的Zope3向导

项目描述

此包提供基于z3c.form的Zope3表单向导概念。

向导

本包的目标是提供一个表单向导。此实现不使用会话。它只提供向导逻辑,向导将更改或添加的数据不是此实现的一部分。如果您想实现一些额外的向导逻辑,您可能需要使用会话并在不同的向导步骤中收集值,并在向导的 doCompletedoFinish 或步骤的 doComplete 方法中创建和添加对象。

所有步骤都可通过自己的URL访问。这使我们能够根据需要缓存每个步骤。每个步骤URL仅在我们允许访问该步骤时才可用。一个步骤是否可访问取决于每个步骤的条件。

因为步骤是适配器,我们可以为已存在的向导注册步骤,或者也可以通过注册一个总是返回FalseUnavailableStep步骤来覆盖现有的步骤。

如果向导已完成,我们将被重定向到确认页面。如果我们再次访问已完成的向导,我们还会被重定向到确认页面。

现在让我们展示它是如何工作的,并设置我们的测试。

表单支持

我们需要加载formui配置,这将确保所有宏都能正确注册。

>>> from zope.configuration import xmlconfig
>>> import z3c.form
>>> import z3c.formui
>>> import z3c.macro
>>> import z3c.template
>>> import zope.browserresource
>>> import zope.component
>>> import zope.i18n
>>> import zope.security
>>> import zope.viewlet
>>> xmlconfig.XMLConfig('meta.zcml', z3c.form)()
>>> xmlconfig.XMLConfig('meta.zcml', z3c.macro)()
>>> xmlconfig.XMLConfig('meta.zcml', z3c.template)()
>>> xmlconfig.XMLConfig('meta.zcml', zope.browserresource)()
>>> xmlconfig.XMLConfig('meta.zcml', zope.component)()
>>> xmlconfig.XMLConfig('meta.zcml', zope.i18n)()
>>> xmlconfig.XMLConfig('meta.zcml', zope.security)()
>>> xmlconfig.XMLConfig('meta.zcml', zope.viewlet)()
>>> xmlconfig.XMLConfig('configure.zcml', z3c.form)()
>>> xmlconfig.XMLConfig('configure.zcml', z3c.formui)()

然后加载z3c.wizard宏配置。

>>> import z3c.wizard
>>> xmlconfig.XMLConfig('configure.zcml', z3c.wizard)()

示例数据设置

让我们定义一个示例内容类。

>>> import zope.interface
>>> import zope.schema
>>> from zope.location.interfaces import ILocation
>>> from zope.schema.fieldproperty import FieldProperty
>>> class IPerson(ILocation):
...     """Person interface."""
...
...     firstName = zope.schema.TextLine(title=u'First Name')
...     lastName = zope.schema.TextLine(title=u'Last Name')
...     street = zope.schema.TextLine(title=u'Street')
...     city = zope.schema.TextLine(title=u'City')
>>> @zope.interface.implementer(IPerson)
... class Person(object):
...     """Person content."""
...
...
...     __name__ = __parent__ = None
...
...     firstName = FieldProperty(IPerson['firstName'])
...     lastName = FieldProperty(IPerson['lastName'])
...     street = FieldProperty(IPerson['street'])
...     city = FieldProperty(IPerson['city'])

为我们的向导设置一个人物。

>>> person = Person()
>>> root['person'] = person
>>> person.__parent__ = root
>>> person.__name__ = u'person'

步骤

让我们定义一些步骤。首先使用一个知道如何存储人物名称的步骤。

>>> from z3c.form import form
>>> from z3c.form import field
>>> from z3c.wizard import step
>>> class PersonStep(step.EditStep):
...     label = u'Person'
...     fields = field.Fields(IPerson).select('firstName', 'lastName')

然后为收集一些地址数据定义另一个步骤。

>>> class AddressStep(step.EditStep):
...     label = u'Address'
...     fields = field.Fields(IPerson).select('street', 'city')

向导

现在我们可以定义我们的Wizard,包括我们的步骤。步骤是命名适配器。让我们使用全局方法addStep来进行步骤设置。

>>> from z3c.wizard import wizard
>>> class IPersonWizard(z3c.wizard.interfaces.IWizard):
...     """Person wizard marker."""
>>> @zope.interface.implementer(IPersonWizard)
... class PersonWizard(wizard.Wizard):
...
...     label = u'Person Wizard'
...
...     def setUpSteps(self):
...         return [
...             step.addStep(self, 'person', weight=1),
...             step.addStep(self, 'address', weight=2),
...             ]

接下来,我们需要将我们的步骤注册为命名的IStep适配器。这可以通过z3c:wizardStep指令完成。现在让我们使用provideAdapter方法定义我们的适配器。

>>> import zope.interface
>>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
>>> from zope.publisher.interfaces.browser import IBrowserRequest
>>> import z3c.wizard.interfaces
>>> zope.component.provideAdapter(
...     PersonStep, (None, IBrowserRequest, None),
...     z3c.wizard.interfaces.IStep, name='person')
>>> zope.component.provideAdapter(
...     AddressStep, (None, IBrowserRequest, None),
...     z3c.wizard.interfaces.IStep, name='address')

我们需要支持我们的请求的div表单层。这是因为我们在步骤中使用的表单部分。因为我们的步骤是表单。

>>> from z3c.formui.interfaces import IDivFormLayer
>>> from zope.interface import alsoProvides
>>> import zope.publisher.browser
>>> import z3c.form.interfaces
>>> @zope.interface.implementer(z3c.form.interfaces.IFormLayer)
... class TestRequest(zope.publisher.browser.TestRequest):
...     pass
>>> request = TestRequest()
>>> alsoProvides(request, IDivFormLayer)

现在我们可以使用我们的向导。我们的向导将始终强制跳转到当前活动步骤。这意味着向导提供了一个browserDefault,它返回默认步骤而不是将向导作为视图渲染。这允许我们使用步骤作为视图和适配器(例如菜单实现使用的)的适配器选择器。向导像一个调度器一样跳转到正确的步骤,而不是作为一个视图本身。

>>> personWizard = PersonWizard(person, request)
>>> personWizard.__parent__ = person
>>> personWizard.__name__ = u'wizard'

现在从向导获取默认视图(步骤)参数。

>>> obj, names = personWizard.browserDefault(request)
>>> obj
<PersonWizard 'wizard'>
>>> names
('person',)

现在跳转到步骤,更新并渲染它。

>>> personStep = obj.publishTraverse(request, names[0])
>>> personStep.update()
>>> print(personStep.render())
<div class="wizard">
    <div class="header">Person Wizard</div>
    <div class="wizardMenu">
      <span class="selected">
          <span>Person</span>
      </span>
      <span>
          <a href="http://127.0.0.1/person/wizard/address">Address</a>
      </span>
    </div>
  <form action="http://127.0.0.1" method="post" enctype="multipart/form-data" class="edit-form" id="form" name="form">
      <div class="viewspace">
          <div class="label">Person</div>
          <div class="required-info">
            <span class="required">*</span>&ndash; required
          </div>
        <div class="step">
          <div id="form-widgets-firstName-row" class="row required">
              <div class="label">
                <label for="form-widgets-firstName">
                  <span>First Name</span>
                  <span class="required">*</span>
                </label>
              </div>
              <div class="widget">
    <input id="form-widgets-firstName" name="form.widgets.firstName" class="text-widget required textline-field" value="" type="text" />
</div>
          </div>
          <div id="form-widgets-lastName-row" class="row required">
              <div class="label">
                <label for="form-widgets-lastName">
                  <span>Last Name</span>
                  <span class="required">*</span>
                </label>
              </div>
              <div class="widget">
    <input id="form-widgets-lastName" name="form.widgets.lastName" class="text-widget required textline-field" value="" type="text" />
</div>
          </div>
        </div>
          <div>
            <div class="buttons">
              <span class="back">
              </span>
              <span class="step">
<input id="form-buttons-apply" name="form.buttons.apply" class="submit-widget button-field" value="Apply" type="submit" />
              </span>
              <span class="forward">
<input id="form-buttons-next" name="form.buttons.next" class="submit-widget button-field" value="Next" type="submit" />
              </span>
            </div>
          </div>
      </div>
  </form>
</div>

如果我们没有完成第一个步骤,我们不能进入下一个步骤。

>>> request = TestRequest(form={'form.buttons.next': 'Next'})
>>> alsoProvides(request, IDivFormLayer)
>>> personWizard = PersonWizard(person, request)
>>> personWizard.__parent__ = person
>>> personWizard.__name__ = u'wizard'
>>> personStep = personWizard.publishTraverse(request, names[0])
>>> personStep.update()
>>> print(personStep.render())
<div class="wizard">
...
  <div class="summary">There were some errors.</div>
...
  <div class="error">Required input is missing.</div>
...
  <div class="error">Required input is missing.</div>
...

如果我们填写了必需的值并点击下一步,我们可以完成这个步骤。

>>> request = TestRequest(form={'form.widgets.firstName': u'Roger',
...                             'form.widgets.lastName': u'Ineichen',
...                             'form.buttons.next': 'Next'})
>>> alsoProvides(request, IDivFormLayer)
>>> personWizard = PersonWizard(person, request)
>>> personWizard.__parent__ = person
>>> personWizard.__name__ = u'wizard'
>>> personStep = personWizard.publishTraverse(request, names[0])
>>> personStep.update()
>>> print(personStep.render())

正如你所看到的,步骤被处理,向导将使用响应重定向概念重定向到下一个步骤。

>>> personWizard.nextURL
'http://127.0.0.1/person/wizard/address'

让我们使用遍历器访问下一个步骤。这将设置下一个步骤。

>>> request = TestRequest()
>>> alsoProvides(request, IDivFormLayer)
>>> personWizard = PersonWizard(person, request)
>>> personWizard.__parent__ = person
>>> personWizard.__name__ = u'wizard'

正如你所看到的,我们看到我们的下一个步骤是地址步骤。

>>> addressStep = personWizard.publishTraverse(request, 'address')
>>> addressStep
<AddressStep 'address'>

更新并渲染它。

>>> addressStep.update()
>>> print(addressStep.render())
<div class="wizard">
    <div class="header">Person Wizard</div>
    <div class="wizardMenu">
      <span>
          <a href="http://127.0.0.1/person/wizard/person">Person</a>
      </span>
      <span class="selected">
          <span>Address</span>
      </span>
    </div>
  <form action="http://127.0.0.1" method="post" enctype="multipart/form-data" class="edit-form" id="form" name="form">
      <div class="viewspace">
          <div class="label">Address</div>
          <div class="required-info">
            <span class="required">*</span>&ndash; required
          </div>
        <div class="step">
          <div id="form-widgets-street-row" class="row required">
              <div class="label">
                <label for="form-widgets-street">
                  <span>Street</span>
                  <span class="required">*</span>
                </label>
              </div>
              <div class="widget">
    <input id="form-widgets-street" name="form.widgets.street" class="text-widget required textline-field" value="" type="text" />
</div>
          </div>
          <div id="form-widgets-city-row" class="row required">
              <div class="label">
                <label for="form-widgets-city">
                  <span>City</span>
                  <span class="required">*</span>
                </label>
              </div>
              <div class="widget">
    <input id="form-widgets-city" name="form.widgets.city" class="text-widget required textline-field" value="" type="text" />
</div>
          </div>
        </div>
          <div>
            <div class="buttons">
              <span class="back">
<input id="form-buttons-back" name="form.buttons.back" class="submit-widget button-field" value="Back" type="submit" />
              </span>
              <span class="step">
<input id="form-buttons-apply" name="form.buttons.apply" class="submit-widget button-field" value="Apply" type="submit" />
              </span>
              <span class="forward">
              </span>
            </div>
          </div>
      </div>
  </form>
</div>

向导和步骤指令

展示如何使用wizardwizardStep指令。注册指令的元配置。

>>> import sys
>>> from zope.configuration import xmlconfig
>>> import z3c.wizard
>>> context = xmlconfig.file('meta.zcml', z3c.wizard)

我们还需要一个自定义向导类。

>>> import z3c.wizard
>>> class MyWizard(z3c.wizard.wizard.Wizard):
...     """Custom wizard"""

使它们在假包custom下可用。

>>> sys.modules['custom'] = type(
...     'Module', (),
...     {'MyWizard': MyWizard})()

在指令中注册向导,使用最少的属性。

>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:wizard
...       name="wizard"
...       class="custom.MyWizard"
...       permission="zope.Public"
...       />
... </configure>
... """, context)

现在定义一个步骤。

>>> import z3c.wizard
>>> class FirstStep(z3c.wizard.step.Step):
...     """First step"""

在自定义模块中注册新的步骤类。

>>> sys.modules['custom'].FirstStep = FirstStep

并在wizardStep指令中使用它们。

>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:wizardStep
...       name="first"
...       wizard="custom.MyWizard"
...       class="custom.FirstStep"
...       permission="zope.Public"
...       />
... </configure>
... """, context)

让我们获取向导。

>>> import zope.component
>>> from zope.publisher.browser import TestRequest
>>> wizard = zope.component.queryMultiAdapter((object(), TestRequest()),
...     name='wizard')

并检查它。

>>> wizard
<MyWizard 'wizard'>
>>> z3c.wizard.interfaces.IWizard.providedBy(wizard)
True

让我们获取向导步骤。

>>> import zope.component
>>> from zope.publisher.browser import TestRequest
>>> firstStep = zope.component.queryMultiAdapter(
...     (object(), TestRequest(), wizard), name='first')

并检查它。

>>> firstStep
<FirstStep 'first'>
>>> firstStep.context
<object object at ...>
>>> firstStep.wizard
<MyWizard 'wizard'>
>>> z3c.wizard.interfaces.IStep.providedBy(firstStep)
True
>>> z3c.wizard.interfaces.IWizard.providedBy(firstStep.wizard)
True

清理自定义模块。

>>> del sys.modules['custom']

变更日志

2.0 (2023-02-10)

  • 添加对Python 3.8、3.9、3.10、3.11的支持。

  • 放弃对Python 2.7、3.4、3.5、3.6的支持。

1.1 (2019-01-27)

  • 添加对Python 3.7的支持。

1.0 (2017-06-16)

  • 添加对Python 3.4至3.6、PyPy2和PyPy3的支持。

0.9.1 (2011-10-28)

  • 使用Python的doctest模块代替已废弃的zope.testing.doctest

  • 允许在步骤完成检查中,值在上下文中不存在。这与z3c.form处理不存在值的方式一致。

0.9.0 (2009-12-29)

  • 在测试中避免使用z3c.form.testing:它依赖于lxml,但这里不需要lxml的功能。

  • 使用 requiredInfo 属性来渲染必填字段的详细信息。此属性返回一个 i18n 消息 ID,使信息可翻译。

0.8.0 (2009-11-30)

  • 调整了依赖项,反映了 zope 包中的更改:使用新包并跳过对 zope.app.publisher 的依赖。

0.7.1 (2009-10-27)

  • 修复了 z3c.form 2.2.0 中的错误。从步骤类中删除了名称定义。这将防止基于 z3c.form 更改而出现错误。

0.7.0 (2009-08-15)

  • 在步骤模板中添加了对字段组的支持。(从 z3c.formui 复制过来。)

  • 有两个名为 header 的金属定义槽。将第一个重命名为 wizard-header

0.6.0 (2009-07-10)

  • 删除对 z3c.i18n 的依赖,因为它实际上并没有被使用,并且我们可以轻松地为“z3c”域重新创建一个消息工厂。

  • 修复了测试,使其与 z3c.form 2.0 一起工作。

  • long_description 中添加了另一个 doctest。

  • 将软件包的邮件列表地址更改为 zope-dev at zope.org,而不是已退休的 zope3-dev。

0.5.0 (2009-02-22)

  • 初始发布

项目详情


下载文件

下载适合您平台的应用程序。如果您不确定选择哪个,请了解有关 安装包 的更多信息。

源分布

z3c.wizard-2.0.tar.gz (25.9 kB 查看散列值)

上传时间 源代码

构建分布

z3c.wizard-2.0-py3-none-any.whl (26.7 kB 查看散列值)

上传时间 Python 3

由以下支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面