跳转到主要内容

Dolmen 和 zeam.form 的表单工具

项目描述

dolmen.forms.base 是一个负责为 zeam.form 表单提供基本功能的包。

从表单到字段

dolmen.forms.base 提供了一些专门用于将字典数据应用到对象字段并触发事件以通知处理程序更新的事件的函数。

应用值

我们创建我们的测试模型

>>> from zope.schema import TextLine, Choice
>>> from zope.interface import Interface, implements

>>> class ICaveman(Interface):
...    name = TextLine(title=u'a name')
...    weapon = Choice(title=u'a weapon',
...                    values=[u'none', u'a club', u'a spear'])

>>> class Neanderthal(object):
...    implements(ICaveman)
...    def __init__(self):
...       self.name = u"no name"
...       self.weapon = u"none"

>>> moshe = Neanderthal()
>>> moshe.name
u'no name'
>>> moshe.weapon
u'none'

我们现在可以使用第一个函数 set_fields_data。它接受字段列表、通过 Fields 集合提取的内容以及数据字典。此调用的结果是一个字典,其中包含字段定义的接口和字段标识符作为值。

>>> from dolmen.forms.base import Fields, set_fields_data

>>> fields = Fields(ICaveman)
>>> for field in fields: print field
<TextLineField a name>
<ChoiceField a weapon>

>>> data = {u'name': u'Grok', u'weapon': u'a club'}

>>> changes = set_fields_data(fields, moshe, data)
>>> print changes
{<InterfaceClass __builtin__.ICaveman>: [u'weapon', u'name']}

>>> moshe.name
u'Grok'
>>> moshe.weapon
u'a club'

数据字典的值可以包含标记,以警告可能的特殊情况:值缺失或没有变化。在这两种情况下,跳过值分配。

>>> from dolmen.forms.base import NO_VALUE, NO_CHANGE
>>> data = {u'name': NO_VALUE, u'weapon': NO_CHANGE}

>>> changes = set_fields_data(fields, moshe, data)
>>> print changes
{}

为事件生成更改属性

生成更改字典的一个优点是可以触发了解更改格式的特定事件。例如,IObjectModifiedEvent 使用更改日志来触发修改字段的重新索引。函数 notify_changes 专门用于通知特定事件已应用的更改。它接受内容、更改字典和事件作为参数。如果省略事件参数,则默认使用 ObjectModifiedEvent。

首先我们生成一个更改字典

>>> data = {u'name': u'Grok', u'weapon': u'a club'}
>>> changes = set_fields_data(fields, moshe, data)
>>> print changes
{<InterfaceClass __builtin__.ICaveman>: [u'weapon', u'name']}

现在我们可以为IObjectModifiedEvent设置一个记录器,以检查更改是否被广播

>>> from zope.component import adapter, provideHandler
>>> from zope.lifecycleevent import IObjectModifiedEvent
>>> from zope.event import subscribers


>>> logger = []

>>> @adapter(ICaveman, IObjectModifiedEvent)
... def changes_broadcasted(content, event):
...    logger.append(event.descriptions)

>>> provideHandler(changes_broadcasted)

现在我们可以将其传递给函数

>>> from dolmen.forms.base import notify_changes
>>> change_log = notify_changes(moshe, changes)

记录器必须已被触发。我们可以检查其值

>>> logger
[(<zope.lifecycleevent.Attributes object at ...>,)]

>>> for attrs in logger[0]:
...     print attrs.interface, attrs.attributes
<InterfaceClass __builtin__.ICaveman> (u'weapon', u'name')

字段更新事件

dolmen.forms.base还提出了一个新的组件定义,该组件可用于将对象的更新过程原子化:IFieldUpdate

为了演示这个IFieldUpdate,我们将实现一个简单的用例,其中实例化一个内容,更改一个值并通知IFieldUpdate组件。为此,我们将使用一个基本的记录器对象

>>> logger = []

一旦完成,我们可以定义两个IFieldUpdate组件。我们将它们实现为命名适配器。我们将通过“getAdapters”调用获取它们

>>> from zope.interface import implementer
>>> from dolmen.forms.base import IFieldUpdate

>>> @implementer(IFieldUpdate)
... @adapter(TextLine, ICaveman)
... def updated_title(field, context):
...    if field.__name__ == u"name":
...       logger.append('Name updated on %r with `%s`' %
...                     (context, getattr(context, field.__name__)))

>>> @implementer(IFieldUpdate)
... @adapter(TextLine, Interface)
... def updated_textfield(field, context):
...    logger.append('A text field has been updated')

由于这些组件是适配器,它们需要命名:我们不希望它们相互覆盖。例如,我们希望它们两个都存在。让我们将它们注册

>>> from zope.component import provideAdapter
>>> provideAdapter(updated_title, name="updatetitle")
>>> provideAdapter(updated_textfield, name="updatetext")

现在,我们开发一个小场景:我们实例化一个内容,为name属性添加一个值并调用适配器

>>> manfred = Neanderthal()
>>> manfred.name = u"Manfred the Mighty"

>>> from zope.component import getAdapters
>>> adapters = getAdapters((ICaveman['name'], manfred), IFieldUpdate)
>>> for adapter in adapters:
...   # We run through the generator
...   pass

>>> for line in logger: print line
Name updated on <Neanderthal object at ...> with `Manfred the Mighty`
A text field has been updated

表单模型

dolmen.forms.base提供了一个表单基类,该类定义了多个有用的方法和覆盖了来自zeam.form的一些默认行为。

>>> from zope.interface import implementedBy
>>> from dolmen.forms.base import ApplicationForm

提供的组件ApplicationForm继承自基类zeam.form组件,并实现了一些额外的方法,使其能够适应您的应用程序,例如,使用flash向给定的源发出消息。它还了解布局

>>> for interface in implementedBy(ApplicationForm):
...     print interface
<InterfaceClass grokcore.layout.interfaces.IPage>
<InterfaceClass zeam.form.base.interfaces.ISimpleForm>
<InterfaceClass zeam.form.base.interfaces.ISimpleFormCanvas>
<InterfaceClass zeam.form.base.interfaces.IGrokViewSupport>
<InterfaceClass zeam.form.base.interfaces.IFormData>
<InterfaceClass zope.publisher.interfaces.browser.IBrowserPage>
<InterfaceClass zope.browser.interfaces.IBrowserView>
<InterfaceClass zope.location.interfaces.ILocation>

由于zeam.form使用Chameleon作为模板引擎,因此我们能够计算当前请求的本地化,以便获取正确的翻译环境

>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()

>>> from zope import site
>>> from zope.location import Location
>>> from zope.location.interfaces import IRoot
>>> from zope.interface import implements

>>> class MyApp(Location, site.SiteManagerContainer):
...     implements(IRoot)
...     __name__ = ''

>>> item = MyApp()
>>> sm = site.LocalSiteManager(item)
>>> item.setSiteManager(sm)

>>> form = ApplicationForm(item, request)
>>> print form.i18nLanguage
None

>>> request = TestRequest(environ={'HTTP_ACCEPT_LANGUAGE': "en,fr"})
>>> form = ApplicationForm(item, request)
>>> print form.i18nLanguage
en

此外,ApplicationForm覆盖了来自zeam.form表单的< cite>extractData方法,以计算接口的不变性。

>>> from grokcore.site.interfaces import IApplication
>>> from zope.interface import alsoProvides
>>> from zope.component.hooks import setSite
>>> setSite(item)
>>> alsoProvides(item, IApplication)
>>> form.application_url()
'http://127.0.0.1'

声明不变性

>>> from zope.schema import Password
>>> from zope.interface import invariant, Interface
>>> from zope.interface.exceptions import Invalid
>>> class IPasswords(Interface):
...     passwd = Password(
...         title=u"Password",
...         description=u"Type the password.",
...         required=True)
...
...     verify = Password(
...         title=u"Password checking",
...         description=u"Retype the password.",
...         required=True)
...
...     @invariant
...     def check_pass(data):
...         if data.passwd != data.verify:
...             raise Invalid(u"Mismatching passwords!")
>>> from zeam.form.base import Fields
>>> from grokcore.component import testing
>>> class MyForm(ApplicationForm):
...     ignoreContent = True
...     ignoreRequest = False
...     fields = Fields(IPasswords)

默认行为

>>> form = MyForm(item, request)
>>> form.update()
>>> form.updateForm()
>>> data, errors = form.extractData()
>>> print data
{'passwd': <Marker NO_VALUE>, 'verify': <Marker NO_VALUE>}
>>> for error in errors:
...     print error.title
Missing required value.
Missing required value.
There were errors.
>>> for error in form.formErrors:
...     print error.title
There were errors.

计算错误

>>> post = TestRequest(form = {'form.field.passwd': u'test',
...                            'form.field.verify': u'fail'})
>>> form = MyForm(item, post)
>>> form.update()
>>> form.updateForm()
>>> data, errors = form.extractData()
>>> print data
{'passwd': u'test', 'verify': u'fail'}

返回的错误是错误组件的集合。使用表单前缀作为标识符,它逻辑上封装了由不变性验证创建的所有错误

>>> for error in form.formErrors:
...     print error.title
Mismatching passwords!

>>> form.errors.get(form.prefix) == form.formErrors[0]
True

混合字段

有两种类型的字段,一种来自zeam.form.base,另一种来自zope.schema。它们都可以在表单中使用,单独或混合使用

>>> from zeam.form.base import Field
>>> from dolmen.forms.base import Fields
>>> class MixedForm(ApplicationForm):
...     ignoreContent = True
...     ignoreRequest = False
...     fields = Fields(IPasswords) + Field(u'Name')

>>> mixedform = MixedForm(item, post)
>>> mixedform.update()
>>> [x.title for x in mixedform.fields]
[u'Password', u'Password checking', u'Name']

>>> mixedform.updateForm()
>>> data, errors = mixedform.extractData()

>>> print form.formErrors
[<Error Mismatching passwords!>]

>>> for error in form.formErrors:
...     print error.title
Mismatching passwords!

更改日志

1.2.1 (2014-11-20)

  • 更新了‘application_url’方法,使其能够与grokcore堆栈的最新更改一起工作。还更新了独立构建包中的版本。

1.2 (2014-11-18)

  • 向表单添加了application_urlflash方法。这导致了使用grokcore.site,现在是一个依赖项。

1.1 (2014-06-18)

  • grokcore.layout取代了megrok.layout。所有导入和测试都已相应更改。

1.0 (2012-10-24)

  • 修复了相关包的新依赖项和更改。

1.0b3 (2010-10-27)

  • 实用方法set_fields_data现在确保,即使数据条目没有相应的字段,它也不会引发错误,正如它应该做的那样。

1.0b2 (2010-10-20)

  • 现在在表单中可以使用zeam.formzope.schema字段。已对行内验证进行了更改,以处理这两种类型。

  • 现在我们使用来自zeam.form.base表单的formErrors而不是我们自己的formError方法。

  • InvariantsValidation现在通过zeam.form.base 1.0引入的dataValidators机制声明。

  • 该包现在在Grok 1.2下进行测试。

1.0b1 (2010-06-25)

  • 该软件包现在使用最新版本的 zeam.form.base,它将 extractDatavalidateData 分离。这使得以更清晰的方式验证不变性,无需覆盖通用代码。

  • 不变性验证中的 DeprecationWarning 已消失。现在使用异常的表示形式,而不是 message 属性。

  • 该软件包现在公开了基 zeam.form 标记。

1.0a2(2010-06-25)

  • ApplicationForm 现在验证接口的不变性。

  • ApplicationForm 现已本地化,因为它提供了上下文 i18nLanguage 属性。

  • 添加了测试

1.0a1(2010-06-02)

  • 添加了一个基础表单模型: ApplicationForm

  • dolmen.forms.base 现在不再使用 z3c.form,而是基于 zeam.form 表单框架

  • 添加了几个辅助函数,用于提取更改属性和通知事件

  • 添加了测试

0.1 (2009-10-25)

  • 初始发布

项目详情


下载文件

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

源分布

支持者: