Dexterity开发手册:第六章 第13节 定制添加或编辑表单

Dexterity开发手册:第六章 第13节 定制添加或编辑表单
介绍采用 z3c.form 包建立定制的表单
至今为止,我们都是采用Dexterity的缺省的添加和编辑表单,在schemata中用form hints 指明表单怎样被创建。对于大多数类型,这些已足够了。但在某些情况下,我们想建立定制的表单或者提供另外的表单。
Dexterity 采用 z3c.form 库建立它的表单,通过plone.z3cform 包集成进来。
注意 plone.z3cform 包要求标准的 z3c.form 表单必须通过一个表单wrapper view被使用。在 Dexterity,这种 wrapper 由来自 plone.directives.form 和 plone.directives.dexterity的form grokkers 语句自动生成。
Dexterity也依赖 plone.autoform, 更具体的是AutoExtensibleForm base class, 该基类是负责处理表单hints和设置z3c.form widgets 和groups (fieldsets)。因此一个定制的表单是这些标准库的一个简单视图。尽管 Dexterity 还提供一些很有帮助的基类,来容易构造基于表单schema和 behaviors的定制表单。
如果想添加不关联到内容类型的表单,参考 z3c.form 文档。为方便起见,你通常可以用plone.directives.form中的基类和schema支持。
编辑表单Edit forms
一个编辑表单是指一种被注册到一个特别的内容类型,并且知道怎样注册它的字段的一种表单。如果该表单命名为 edit, 它将替换缺省的被用更通常的IDexterityContent 接口注册的编辑表单。
Dexterity 提供标准的编辑表单基类,这将提供缺省的表单按钮、labels等。这应当被注册为一个内容类型schema (而不是一个 class)。为了创建一个等同于缺省的编辑表单,我们可以:
class EditForm(dexterity.EditForm):  grok.context(IFSPage)上面这个 dexterity 模块是 plone.directives.dexteritygrok 模块是 five.grok.
该表单的缺省名称是 edit, 但是我们可以通过 grok.name()指定不同的名称。缺省的 permission 是 cmf.ModifyPortalContent, 但是可以通过 grok.require()指定不同的权限。我们还能通过 grok.layer()来指定该表单为特别的浏览器层(browser layer)
上面的样例由于是缺省的最简单的表单,当然没什么意思。然而我们可以从这里出发,开始更改字段和值,例如,我们可以:
  • 覆盖schema 属性告诉 plone.autoform 用一个不同的内容schema 接口 (采用不同的form hints)
  • 覆盖 additional_schemata 属性告诉 plone.autoform to use different supplemental schema interfaces. The default is to use all behavior interfaces that provide the IFormFieldProvider marker from plone.directives.form.
  • 覆盖 labeldescription 属性为表单提供不同的标题和描述。
  • 直接设置 z3c.form fieldsgroups 属性。
  • 覆盖 updateWidgets() 方法来修改widget属性,或者用一个其他的 update*() 方法,在这个字段执行其他处理。In most cases, these require us to call the super version at the beginning. 参考 plone.autoformz3c.form 文档 to learn more about the sequence of calls that eminate from the form update() method in the z3c.form.form.BaseForm class.
内容添加序列Content add sequence
添加表单类似于编辑表单,它们被建立来自于内容的schema 和附加于其上的行为的schemata 。然而,添加表单用于创建一个内容对象,它需要知道 相关 portal_type to use.
你应该已意识到在portal_types tool里的 FTIs能够通过web界面修改。甚至可以通过web界面创建新的类型,重用已存的 classes 和 factories.
为了这个原因,添加表单被通过名称空间遍历适配器: ++add++来查询。 你已经注意到已有的添加表单的 URLs 。实际发生的事情如下:
  • Plone 呈现添加菜单
    • To do so, it looks, among other places, for actions in the folder/add category. This category is provided by the portal_types tool.
    • The folder/add action category is constructed by looking up the add_view_expr property on the FTIs of all addable types. This is a TALES expression telling the add menu which URL to use.
    • The default add_view_expr in Dexterity (and CMF 2.2) is string:${folder_url}/++add++${fti/getId}. That is, it uses the ++add++ traversal namespace with an argument containing the FTI name.
  • 一个用户点击菜单中的一个条目,即取得一个类似于 /path/to/folder/++add++my.type 的URL
    • 这个 ++add++ 名称空间适配器查看给定名称的类型的 FTI ,并且获取到它的 factory 属性。
    • 一个FTI的这个 factory 属性给出了一个提供特别的zope.component.interfaces.IFactory接口的utility,这个稍后将被用来构造内容对象的一个实例 。 Dexterity automatically registers a factory instance for each type, with a name that matches the type name, although it is possible to use an existing factory name in a new type. This allows administrators to create new "logical" types that are functionally identical to an existing type.
    • The ++add++ namespace adapter looks up the actual form to render as a multi-adapter from (context, request, fti) to Interface with a name matching the factory property. Recall that a standard view is a multi-adapter from (context, request) to Interface with a name matching the URL segment for which the view is looked up. As such, add forms are not standard views, because they get the additional fti parameter when constructed.
    • If this fails, there is no custom add form for this factory (as is normally the case). The fallback is an unnamed adapter from (context, request, fti). The default Dexterity add form is registered as such an adapter, specific to the IDexterityFTI interface.
  • The form is rendered like any other z3c.form form instance, and is subject to validation, which may cause it to be loaded several times.
  • Eventually, the is successfully submitted. At this point:
    • 这个标准的 AddForm 基类将从FTI引用中查找 factory ,然后调用它创建一个实例。
    • 缺省的 Dexterity factory查看FTI 中的klass 属性 (class is a reserved word in Python…) 以决定实际被采用的内容类,用这个类创建一个对象,并初始化它。
    • 新创建的类实例(内容对象)的 portal_type 属性设置为 FTI的名称。也即,如果 通过web创建的FTI的名称是 "logical type" ,但采用已存的 factory,那么这个新实例的 portal_type 将被设置为 "logical type".
    • 该对象用表单提交的值初始化
    • 一个IObjectCreatedEvent 被触发
    • 该对象被添加到它的容器中
    • 用户被重定向到在FTI的指定的immediate_view 属性指明的视图
This sequence is pretty long, but thankfully we rarely have to worry about it. In most cases, we can use the default add form, and when we can't, creating a custom add form is no more difficult than creating a custom edit form. The add form grokker take care of registering the add view appropriately.
定制添加表单
As with edit forms, Dexterity provides a sensible base class for add forms that knows how to deal with the Dexterity FTI and factory.
A custom form replicating the default would look like this:
class AddForm(dexterity.AddForm): grok.name('example.fspage')这儿的名称应该匹配 factory 名称。 缺省Dexterity类型有一个和FTI名称一样的 factory 。如果没有这样的factory存在 (例如你没有注册一个定制的IFactory utility), 当FTI被安装时,一个本地的 factory utility 将被创建,并被Dexterity管理。
也请注意,这儿我们不指定一个上下文。添加表单总是被注册为任意 IFolderish 上下文。我们可以用 grok.layer() 指定一个层,用grok.require()指定一个权限代替默认的 cmf.AddPortalContent 权限。
如果添加表单的权限不同于FTI中设置的add_permission 权限,那么这个用户需要全部的两个权限来查看表单和添加内容。由于这个原因,大多数的添加表单通常用 cmf.AddPortalContent 权限。如果用户没有在FTI指定的add permission,这个添加菜单将不呈现到这个类型的添加链接。 even if this is different to cmf.AddPortalContent.
作为编辑表单,我们可以通过覆盖 z3c.formplone.autoform 的属性和方法来定制表单。关于添加表单和更多信息参考 z3c.form 文档。
设置