跳转到主要内容

为`dolmen.content`提供CRUD表单和操作

项目描述

dolmen.forms.crud是一个模块,它帮助开发人员使用Grokzeam.formdolmen.content创建他们的C.R.U.D表单。它提供了一组基类来添加、编辑和访问内容。它提供了适配器来自定义表单的字段。

添加视图

dolmen.forms.crud提供了对“添加”操作的抽象。它允许在容器级别进行插件式扩展并处理命名和持久化。更具体地说,它是一个命名适配器,将查询添加表单、检查约束、选择名称(使用INameChooser)并最终,如果一切顺利,将其添加到上下文中。

基础添加视图默认以名为“add”的命名可遍历适配器注册。它使用以下模式:++add++factory_name。 factory_name 必须是 dolmen.content.IFactory 组件的名称。

首先,我们创建一个容器,我们将在此容器中测试添加视图

>>> import dolmen.content
>>> from dolmen.forms.crud.tests import Sietch

>>> sietch = Sietch()
>>> dolmen.content.IContent.providedBy(sietch)
True

>>> from zope.site.hooks import getSite
>>> root = getSite()
>>> root['sietch'] = sietch

容器创建后,添加视图应该可用并运行。让我们快速了解一下

>>> from zope.component import getMultiAdapter
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> addingview = getMultiAdapter((sietch, request), name='add')
>>> addingview
<dolmen.forms.crud.addview.Adder object at ...>

添加视图组件会明确检查对工厂的安全要求。为了测试这种行为,我们设置了两个账户。‘zope.manager’拥有所有权限,而‘zope.manfred’只有‘zope.View’凭证。我们的工厂明确要求调用时必须有‘zope.ManageContent’权限。让我们尝试用Manfred访问它

>>> import zope.security.management as security
>>> from zope.security.testing import Principal, Participation

>>> manager = Principal('zope.manager', 'Manager')
>>> manfred = Principal('zope.manfred', 'Manfred')

>>> security.newInteraction(Participation(manfred))
>>> addingview.traverse('fremen', [])
Traceback (most recent call last):
...
Unauthorized: <class 'dolmen.forms.crud.tests.Fremen'> requires the 'zope.ManageContent' permission.

>>> security.endInteraction()

Manfred没有权限,但是Manager应该能够成功访问添加视图

>>> security.newInteraction(Participation(manager))
>>> addingview.traverse('fremen', [])
Traceback (most recent call last):
...
NotFound: Object: <dolmen.forms.crud.tests.Sietch object at ...>, name: 'fremen'

添加视图对我们的条目可用。尽管如此,因为我们没有注册添加表单,如果我们尝试访问当前的工厂,将引发NotFound错误

让我们创建并注册一个非常基本的通用CRUD添加表单

>>> import dolmen.forms.crud as crud
>>> class AddForm(crud.Add):
...     '''Generic add form.
...     '''

>>> import grokcore.component
>>> grokcore.component.testing.grok_component('addform', AddForm)
True

>>> addform = addingview.traverse('fremen', [])
>>> addform
<dolmen.forms.crud.tests.AddForm object at ...>

当我们遍历到‘fremen’工厂时,我们的添加表单被返回。

太好了。我们的添加视图准备就绪,可以使用了。在测试添加表单本身之前,让我们尝试一下 add 方法

>>> from dolmen.forms.crud.tests import Fremen

>>> naib = Fremen()
>>> added_item = addingview.add(naib)
>>> added_item
<dolmen.forms.crud.tests.Fremen object at ...>

创建的内容被正确定位并持久化

>>> added_item.__name__
u'Fremen'
>>> added_item.__parent__ is sietch
True

事实上,IAdding组件应该始终可定位。方便的是,您可以访问位置信息

>>> addingview.__parent__
<dolmen.forms.crud.tests.Sietch object at ...>
>>> addingview.__name__
u''

add 方法检查是否遵守了约束。如果容器已定义限制或违反了某些接口合同,我们将得到一个错误

>>> from dolmen.forms.crud.tests import Harkonnen

>>> rabban = Harkonnen()
>>> addingview.add(rabban)
Traceback (most recent call last):
...
InvalidItemType: (<...Sietch object at ...>, <...Harkonnen object at ...>, (<InterfaceClass dolmen.forms.crud.tests.IDesertWarrior>,))

添加视图的 add 方法可以从添加表单中调用以委派添加操作。通用的添加视图已经处理了常见的操作,如命名和持久化。

通用表单

dolmen.forms.crud 提供了一组可立即使用的基类,这些类将根据 dolmen.content 架构自动生成表单。

dolmen.forms.crud 表单具有布局感知性(有关更多信息,请参阅 megrok.layout)。因此,我们需要注册一个基本布局来渲染我们的表单

>>> from grokcore.layout import Layout
>>> from zope.interface import Interface

>>> class GenericLayout(Layout):
...     grokcore.component.context(Interface)
...
...     def render(self):
...         return self.view.content()

>>> grokcore.component.testing.grok_component('layout', GenericLayout)
True

测试的上下文是我们之前创建的内容

>>> naib
<dolmen.forms.crud.tests.Fremen object at ...>
>>> naib.__parent__
<dolmen.forms.crud.tests.Sietch object at ...>

创建

添加表单实现与添加视图紧密相关。由于添加表单的行为在上述内容中已经基本覆盖,我们只需测试表单本身的字段和标签是否存在

>>> addform = addingview.traverse('fremen', [])
>>> addform
<dolmen.forms.crud.tests.AddForm object at ...>

>>> print addform.label
Add: Fremen Warrior

>>> addform.fields.keys()
['title', 'water']

>>> addform.updateForm()
>>> for action in addform.actions: print action
<AddAction Add>
<CancelAction Cancel>

>>> security.endInteraction()

值现在应该已设置

更新

>>> class EditForm(crud.Edit):
...     '''Generic edit form.
...     '''

>>> grokcore.component.testing.grok_component('editform', EditForm)
True

可以通过从 Edit 基类派生来简单地注册一个编辑表单

>>> grokcore.component.name.bind().get(EditForm)
'edit'

默认情况下,注册的 Edit 表单名称为‘edit’

>>> post = TestRequest(form={
...     'form.field.water': '25',
...     'form.field.title': u'Stilgar',
...     'form.action.update': u'Update'},
...     REQUEST_METHOD='POST',
...     )

>>> security.newInteraction(post)

>>> editform = getMultiAdapter((naib, post), name='edit')
>>> editform
<dolmen.forms.crud.tests.EditForm object at ...>

>>> editform.updateForm()
>>> for action in editform.actions: print action
<UpdateAction Update>
<CancelAction Cancel>

>>> editform.fields.keys()
['title', 'water']

注册此表单后,我们可以检查所有字段是否已准备好编辑

>>> naib.title
u'Stilgar'
>>> naib.water
25

>>> security.endInteraction()

读取

一种特殊的表单允许您显示内容

>>> class DefaultView(crud.Display):
...     '''Generic display form.
...     '''

>>> grokcore.component.testing.grok_component('display', DefaultView)
True

>>> security.newInteraction(TestRequest())

>>> view = getMultiAdapter((naib, request), name='defaultview')
>>> view
<dolmen.forms.crud.tests.DefaultView object at ...>

显示表单从字段列表中删除了‘title’。该特定属性直接由模板使用

>>> view.fields.keys()
['water']

显示表单没有操作

>>> len(view.actions)
0

dolmen.forms.crud 为该表单提供了一个非常基本的模板。正如我们所看到的,标题属性用作页面的HTML标题(h1)

>>> print view()
<form action="http://127.0.0.1" method="post"
      enctype="multipart/form-data">
  <h1>Stilgar</h1>
  <div class="fields">
    <div class="field">
        <label class="field-label" for="form-field-water">Number water gallons owned</label>
        <span class="field-required">(required)</span>
        <br />
      25
    </div>
  </div>
</form>

>>> security.endInteraction()

删除

删除表单是一个简单的表单,没有字段,只提供“确认”操作

>>> class DeleteForm(crud.Delete):
...     '''Generic delete form.
...     '''

>>> grokcore.component.testing.grok_component('delete_form', DeleteForm)
True

>>> deleteform = getMultiAdapter((naib, request), name='deleteform')
>>> deleteform
<dolmen.forms.crud.tests.DeleteForm object at ...>

>>> deleteform.updateForm()
>>> for action in deleteform.actions: print action
<DeleteAction Delete>
<CancelAction Cancel>

>>> len(deleteform.fields)
0

确认后,表单将尝试删除对象

>>> post = TestRequest(form={
...     'form.action.delete': u'Delete'},
...     REQUEST_METHOD='POST',
...     )

>>> security.newInteraction(post)

>>> list(sietch.keys())
[u'Fremen']

>>> deleteform = getMultiAdapter((naib, post), name='deleteform')
>>> deleteform.updateForm()

>>> from zope.i18n import translate
>>> translate(deleteform.status, context=post)
u'The object has been deleted.'

>>> list(sietch.keys())
[]

>>> deleteform.response.getStatus()
302
>>> deleteform.response.getHeader('location')
'http://127.0.0.1/sietch'

>>> security.endInteraction()

没有Dublin Core的通用表单

上述测试使用了定义标题的内容,让我们验证它仍然适用于裸内容。

>>> sietch = root['sietch']

创建

表单自定义

要自定义表单,通常的解决方案是派生它们并与派生类一起工作。dolmen.forms.crud 提出一个新组件来自定义您的表单。通过 IFieldsCustomization 接口定义,它是一个适配器,允许您在字段级别进行交互。

IFieldsCustomization 中,自定义发生在 __call__ 级别。表单在更新对象字段的同时,会查询一个 IFieldsCustomization 适配器并调用它,将字段作为参数传递。

让我们实现一个示例

>>> class RemoveWater(crud.FieldsCustomizer):
...    grokcore.component.adapts(Fremen, crud.Add, None)
...
...    def __call__(self, fields):
...       """Alters the form fields"""
...       return fields.omit('water')

>>> from zope.interface import verify
>>> verify.verifyClass(crud.IFieldsCustomization, RemoveWater)
True

我们现在可以注册并测试自定义

>>> grokcore.component.testing.grok_component('removewater', RemoveWater)
True

>>> security.newInteraction(Participation(manager))

>>> addform = addingview.traverse('fremen', [])
>>> for field in addform.fields: print field
<TextLineField Name of the warrior>

这里有一个重要的事情需要注意:已经为‘Fremen’组件注册了‘RemoveWater’适配器。为了能够查找合适的适配器,添加表单使用了一个特殊的查找函数:dolmen.forms.crud.utils.queryClassMultiAdapter

我们可以测试一个更复杂的例子,返回一个新的Fields实例

>>> import dolmen.forms.base
>>> from dolmen.forms.crud.utils import getSchemaFields

>>> class AddFieldToView(crud.FieldsCustomizer):
...    grokcore.component.adapts(Fremen, crud.Display, None)
...
...    def __call__(self, fields):
...       """Returns a new instance of Fields.
...       """
...       schema = dolmen.content.get_schema(self.context)
...       if schema:
...           return dolmen.forms.base.Fields(*schema)
...       return dolmen.forms.base.Fields()

>>> grokcore.component.testing.grok_component('viewer', AddFieldToView)
True

检查字段,我们应该得到Fremen模式定义的所有字段

>>> view = getMultiAdapter((naib, request), name='defaultview')
>>> view.fields.keys()
['title', 'water']

>>> security.endInteraction()

事件和字段更新

当使用通用的dolmen.forms.crud表单时,会为您触发一些事件。这些事件代表了被操作对象的生存周期。

为了检查所有触发的事件,我们可以设置一个简单的日志列表和一个通用的事件处理器

>>> from zope.component import provideHandler
>>> from zope.lifecycleevent import IObjectModifiedEvent
>>> logger = []

>>> def event_logger(object, event):
...   logger.append(event)

>>> provideHandler(event_logger, (Fremen, IObjectModifiedEvent))

编辑事件

让我们用同样的检查方法来检查编辑表单

>>> logger = []

我们提供更新所需的数据

>>> request = TestRequest(form={
...     'form.field.water': '10',
...     'form.field.title': u'Sihaya',
...     'form.action.update': u'Update'},
...     REQUEST_METHOD='POST',
...     )

>>> security.newInteraction(request)

>>> chani = Fremen()
>>> root['chani'] = chani

>>> editform = getMultiAdapter((chani, request), name='edit')
>>> editform.updateForm()

我们检查触发的事件

>>> for event in logger: print event
<...ObjectModifiedEvent object at ...>

深入来看,我们可以检查更新后的字段是否在事件描述中正确设置

>>> for desc in logger[0].descriptions:
...   print "%r: %s" % (desc.interface, desc.attributes)
<InterfaceClass dolmen.forms.crud.tests.IDesertWarrior>: ('water', 'title')

>>> chani.title
u'Sihaya'
>>> chani.water
10

>>> security.endInteraction()

字段更新

dolmen.forms.base提供了一个新组件的描述,可以用于原子化对象的更新过程:IFieldUpdate。在dolmen.forms.crud中有一个实现,使用事件处理器,监听ObjectModifiedEvent和ObjectCreatedEvent。

>>> updates = []

>>> from zope.schema import TextLine
>>> from zope.component import adapter, provideAdapter
>>> from zope.interface import implementer
>>> from dolmen.forms.base import IFieldUpdate

>>> @implementer(IFieldUpdate)
... @adapter(Fremen, TextLine)
... def updated_textfield(context, field):
...    updates.append((context, field))

>>> provideAdapter(updated_textfield, name="updatetext")

在使用添加表单时,IFieldUpdate适配器应该在对象创建期间被调用

>>> request = TestRequest(form={
...     'form.field.title': u'Liet',
...     'form.action.add': u'Add'},
...     REQUEST_METHOD='POST',
...     )

>>> request.setPrincipal(manager)
>>> interaction = security.newInteraction(request)

>>> desert = root['desert'] = dolmen.content.Container()
>>> addingview = getMultiAdapter((desert, request), name='add')
>>> addform = addingview.traverse('fremen', [])
>>> addform.updateForm()

>>> kynes = desert['Fremen']
>>> kynes
<dolmen.forms.crud.tests.Fremen object at ...>
>>> kynes.title
u'Liet'

>>> print updates
[(<dolmen.forms.crud.tests.Fremen object at ...>,
<zope.schema._bootstrapfields.TextLine object at ...>)]

>>> security.endInteraction()

我们也可以为编辑表单做同样的事情

>>> updates = []

>>> request = TestRequest(form={
...     'form.field.water': '50',
...     'form.field.title': u'Imperial weather specialist',
...     'form.action.update': u'Update'},
...     REQUEST_METHOD='POST',
...     )

>>> request.setPrincipal(manager)
>>> security.newInteraction(request)

>>> editform = getMultiAdapter((kynes, request), name='edit')
>>> editform.updateForm()

>> kynes.title
u'Imperial weather specialist'

>>> updates
[(<dolmen.forms.crud.tests.Fremen object at ...>, <zope.schema._bootstrapfields.TextLine object at ...>)]

如果没有注册IFieldUpdate适配器,更新字段不应该做任何事情

>>> updates = []

 >>> request = TestRequest(form={
 ...     'form.field.water': '40',
 ...     'form.action.update': u'Update'},
 ...     REQUEST_METHOD='POST',
 ...     )

 >>> editform = getMultiAdapter((kynes, request), name='edit')
 >>> editform.updateForm()

 >>> updates
 []

变更日志

1.0 (2012-10-24)

  • 修复了依赖和测试,反映了相关包中的更改。

1.0b5 (2011-02-16)

  • 我们现在使用来自dolmen.content的新get_schema实用函数来获取schema值。这允许以标准化的方式处理schema检索。

1.0b4 (2011-02-16)

  • 修复了如果IContent对象没有定义schema时触发事件会失败的问题。

1.0b3 (2011-02-14)

  • 添加了一个实用函数,它根据表单、上下文和省略的字段名称列表返回一个Fields实例。

  • 现在支持没有schema的对象或类。

  • 我们现在使用表单的< cite >getContent方法来获取编辑、显示、删除表单的上下文。

1.0b2 (2011-02-14)

  • 适应了最新的dolmen.content更改。

  • 标题现在是通过来自zope.dublincoreDCDescriptiveProperties获得的(用于标签)。

  • 适应了最新的zeam.form.base更改的测试。

1.0b1 (2010-07-13)

  • 使用最新的dolmen.content更改,以显示合理的表单标签。

  • 添加了正确的翻译。

  • 表单模式现在使用模式标记。

1.0a3 (2010-06-25)

  • 添加了缺少的翻译(法语)

  • 操作现在使用表单的< cite >getContent方法,以获取适当的形式内容。此外,现在测试内容,以处理DataManagers。导入修复:删除操作现在表现正确。

  • 使用最新的dolmen.forms.base版本,改进了错误处理和数据处理以及通知。

1.0a2 (2010-06-05)

  • 向输入表单添加了取消操作。

  • 修复了提交失败时错误设置不正确的错误。

1.0a1 (2010-06-03)

  • 该包现在使用< cite >zeam.form而不是< cite >z3c.form。

  • 添加了国际化(法语,英语)

  • IObjectInitializedEvent已被移除,因为我们现在使用dolmen.content >= 0.3.1。它已被简单的IObjectAddedEvent所取代。IFieldUpdate触发器相应地进行了修改。

0.4.0 (2010-02-22)

  • 清理了依赖和测试。该包现在是100% zope.app免费的。

0.3.0 (2009-11-02)

  • 移除了对zope.app.container的依赖。

  • 升级到使用ZTK版本(1.0dev)。

0.2.1 (2009-11-02)

  • 删除表单现在使用类级别消息来处理成功和失败。

  • 更新了测试以覆盖删除错误。

  • 在删除操作后更正了重定向URL。

0.2.0 (2009-11-02)

  • 添加了DeleteForm

  • 为所有表单添加了标题

  • 更正了EditForm中的双重nextURL方法。

0.1.0 (2009-10-26)

  • 初始发布

项目详情


下载文件

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

源分发

dolmen.forms.crud-1.0.tar.gz (24.1 kB 查看哈希值)

上传时间

由...