为`dolmen.content`提供CRUD表单和操作
项目描述
dolmen.forms.crud是一个模块,它帮助开发人员使用Grok、zeam.form和dolmen.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.dublincore的DCDescriptiveProperties获得的(用于标签)。
适应了最新的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的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 4e7fcd350a42fb25949606c2d4052be27640d7df21fffa44b8800b2334806cc7 |
|
MD5 | 7b1e6d993557626074299ffe218afbf1 |
|
BLAKE2b-256 | ee70ca0c19ab78b584eaded2adc5012276801e1a3e09172db9499d472d576ba4 |