Dexterity开发手册:第四章 Schema驱动内容类型

Dexterity开发手册:第四章 Schema驱动内容类型
[align=left][font=宋体]创建一个基于[/font]schema[font=宋体]的极小类型[/font][/align][align=left]4.1 [font=宋体]关于[/font]schema[/align][align=left]4.2 [font=宋体]关于[/font]FTI[/align][align=left]4.3 [font=宋体]测试这个类型[/font][/align]

[align=left]4.1关于schema[/align][align=left][font=宋体]为这个类型编写一个[/font]schema[/align]
[align=left][font=宋体]一个简单的[/font]Dexterity[font=宋体]类型包含一个[/font]schema[font=宋体]和一个[/font]FTI[font=宋体]([/font]Factory Type Information,[font=宋体]这个对象在[/font]ZMI[font=宋体]的[/font]portal_types[font=宋体]中配置),我们将在此创建一个[/font]schemata[font=宋体],同时将在下一节创建[/font]FTI[font=宋体]。[/font][/align][align=left][font=宋体]每一个[/font]schema[font=宋体]通常是在一个独立的模块里。因此,我们将添加三个文件到我们的产品:[/font]presenter.py,program.py,[font=宋体]和[/font]session.py[font=宋体]。每一个都将以一个[/font]schema[font=宋体]接口开始。[/font][/align][font=宋体]
首先,我们将定义一个信息工厂来帮助未来国际化的软件包。呈现在用户面前的每一个字符都应该包裹在[/font]_()[font=宋体]里面就像如下图所示的标题和描述。[/font][font=宋体]这个信息工厂存在于包的根文件[/font]_init_.py[font=宋体]里面。[/font]from zope.i18nmessageid import MessageFactory _ =MessageFactory("example.conference")[font=宋体]注意我们怎么使用包的名称作为翻译域。[/font][align=left][font=宋体]现在可以为我们的三种类型定义[/font]schemata。[/align]
[align=left][font=宋体]对于[/font]Presenter[font=宋体]类型,[/font]presenter.py[font=宋体]看起来像这样的:[/font][/align]from five import grok from zope import schema from plone.directives import form, dexterity from plone.app.textfield import RichText from plone.namedfile.field import NamedImage from example.conference import _ class IPresenter(form.Schema): """A conference presenter. Presenters can be added anywhere. """ title = schema.TextLine( title=_(u"Name"), ) description = schema.Text( title=_(u"A short summary"), ) bio = RichText( title=_(u"Bio"), required=False, ) picture = NamedImage( title=_(u"Picture"), description=_(u"Please upload an image"), required=False, )[font=宋体]注意我们是如何为[/font]name[font=宋体]和[/font]summary[font=宋体]使用字段名的标题和描述的。我们这么做为[/font]Plone[font=宋体]的文件夹清单和检索中使用的默认的标题和描述数据元提供值。一般情况下,每一个类型都应该有一个标题字段,尽管它可以被行为(后面将会讲述)来规定。[/font]
[align=left][font=宋体]对于[/font]Program[font=宋体]类型,[/font]program.py[font=宋体]看起来这样的:[/font][/align]from five import grok from zope import schema from plone.directives import form, dexterity from plone.app.textfield import RichText from example.conference import _ class IProgram(form.Schema): """A conference program. Programs can contain Sessions. """ title = schema.TextLine( title=_(u"Program name"), ) description = schema.Text( title=_(u"Program summary"), ) start = schema.Datetime( title=_(u"Start date"), required=False, ) end = schema.Datetime( title=_(u"End date"), required=False, ) details = RichText( title=_(u"Details"), description=_(u"Details about the program"), required=False, )[align=left][font=宋体]最后,[/font]Session[font=宋体]类型的[/font]session.py[font=宋体]看起来这样的:[/font][/align]from five import grok from zope import schema from plone.directives import form, dexterity from plone.app.textfield import RichText class ISession(form.Schema): """A conference session. Sessions are managed inside Programs. """ title = schema.TextLine( title=_(u"Title"), description=_(u"Session title"), ) description = schema.Text( title=_(u"Session summary"), ) details = RichText( title=_(u"Session details"), required=False )[font=宋体]注意我们还没有添加关于[/font]speakers or tracks[font=宋体]的信息。我们要覆盖词汇与参考在我们添加这些信息的时候。[/font]
[align=left]Schema [font=宋体]接口与其他接口对比[/font][/align][align=left][font=宋体]你也许已经注意到,每个[/font]schema[font=宋体]本质上就是一个带有一些字段的接口([/font]zope.interface.interface[font=宋体])。这些标准字段都可以在[/font]zope.schema.package[font=宋体]里面找到。你应该从它的接口([/font]parts/omelette/zope/schema/interfaces.py[font=宋体])来有效的学习各种各样的[/font]schema[font=宋体]字段,同时回顾这个包的一些网上文档。当你需要一个文件字段时,可以查阅[/font]plone.namefile[font=宋体],当你需要使用引用时,你可以查阅[/font]z3c.relationfield[font=宋体],当你需要一个支持富文本模式的[/font]WYSIWYG[font=宋体]编辑器是,可以查找[/font]plone.app.textfield[font=宋体]。我们稍后会在该手册中覆盖这些字段类型。它们也可以在最后的参考中找到。[/font][/align][font=宋体]
和标准的借口不一样,然而,我们源于[/font]form.Schema[font=宋体](事实上的是[/font]plone.directives.form.Schema[font=宋体])。这仅仅是一个允许我们去添加一些形式提示到接口的标记接口,然后被[/font]Dexterity[font=宋体](事实上是[/font]the plone.autoform package[font=宋体])用来构建形式。查看[/font]plone.directives.form[font=宋体]文件可以来学习更多的关于各种提示。最常用的是[/font]form.fieldset()[font=宋体],来定义大量的字段,[/font]form.widget()[font=宋体],用来为一个特殊的字段设置一个部件,和[/font]form.omit()[font=宋体]用来从形式上隐藏一个或多个字段。我们稍后将会看到一些这方面的例子在这个手册里。[/font]

[align=left]4.2关于FTI[/align][align=left] [font=宋体]为该类型添加一个工厂类型信息对象。[/font][/align][align=left][font=宋体]有了合适的[/font]Schema[font=宋体],我们仅仅需要让我们的类型是可安装的。我们使用[/font]GenericSetup[font=宋体]来做这个。[/font][/align][align=left][font=宋体]首先,我们添加一个[/font]types.xml[font=宋体]文件到[/font]profiles/default[font=宋体]:[/font][/align]<object name="portal_types"> <object name="example.conference.presenter" meta_type="Dexterity FTI" /> <object name="example.conference.program" meta_type="Dexterity FTI"/> <object name="example.conference.session" meta_type="Dexterity FTI"/> </object>[align=left][font=宋体]我们使用包名作为前缀和小写的类型名称,以创建一个唯一的名称。重要的是[/font]meta_type[font=宋体]是一个[/font]Dexterity FTI[font=宋体]。[/font][/align][font=宋体]然后我们需要为每个类型添加一个[/font]XML[font=宋体]文件,文件名称和类型名称匹配。[/font][align=left][font=宋体]首先,我们添加一个目录[/font] profiles/default/types[font=宋体],然后添加下面的内容:[/font][/align]
[align=left][font=宋体]对[/font]Presenter[font=宋体]类型,我们有[/font]example.conference.presenter.xml[font=宋体]:[/font][/align]<?xml version="1.0"?> <object name="example.conference.presenter" meta_type="Dexterity FTI" i18n:domain="example.conference" xmlns:i18n="http://xml.zope.org/namespaces/i18n"> <!-- Basic metadata --> <property name="title" i18n:translate="">Presenter</property> <property name="description" i18n:translate="">A person presenting sessions</property> <property name="content_icon">user.gif</property> <property name="allow_discussion">True</property> <property name="global_allow">True</property> <property name="filter_content_types">True</property> <property name="allowed_content_types" /> <!-- schema interface --> <property name="schema">example.conference.presenter.IPresenter</property> <!-- class used for content items --> <property name="klass">plone.dexterity.content.Item</property> <!-- add permission --> <property name="add_permission">cmf.AddPortalContent</property> <!-- enabled behaviors --> <property name="behaviors"> <element value="plone.app.content.interfaces.INameFromTitle" /> </property> <!-- View information --> <property name="default_view">view</property> <property name="default_view_fallback">False</property> <property name="view_methods"> <element value="view"/> </property> <!-- Method aliases --> <alias from="(Default)" to="(dynamic view)"/> <alias from="edit" to="@@edit"/> <alias from="sharing" to="@@sharing"/> <alias from="view" to="(selected layout)"/> <!-- Actions --> <action title="View" action_id="view" category="object" condition_expr="" url_expr="string:${object_url}" visible="True"> <permission value="View"/> </action> <action title="Edit" action_id="edit" category="object" condition_expr="" url_expr="string:${object_url}/edit" visible="True"> <permission value="Modify portal content"/> </action> </object>[font=宋体]有相当数量的样板,这里实际上可以省略的,因为[/font]Dexterity FTI[font=宋体]默认会最注意这个方面的。然而,看到这些可供选择的方案是有用的,这样你知道你可以改变它。[/font]
[align=left][font=宋体]重点是:[/font][/align]
  • 根元素的名称属性必须匹配types.xml和文件名称。
  • 我们使用包的名称再次作为翻译名称,通过i18n:domain
  • 我们为这个类型设置一个标题和描述
  • 我们也指定一个图标。在此,我们使用一个来自Plone的plone_images皮肤的图标。稍后你将会学习更多的静态资源。
  • 我们把global_allow设置为True。这意味着这个类型在标准文件夹里将是可添加的。
  • Schema接口是被schema属性引用的。
  • 我们设置Klass属性到标准的plone.dexterity.content.Item。也有plone.dexterity.content.Container
  • 我们指定添加权限的名称。默认的cmf.AddPortalContent应该被使用除非你配置一个定制的权限。定制的权限稍后会在手册中讲到。
  • 我们添加一个行为。行为是可以重复使用的各个方面提供语义和(或)schema字段的。在这里我们添加INameFromTitle行为,将给我们内容对象一个基于这个Title属性的可读的标示符。稍后我们将会讲到其他的行为。
[align=left][font=宋体]在[/font]example.conference.session.xml[font=宋体]里面,[/font]Session[font=宋体]类型是非常相似的:[/font][/align]<?xml version="1.0"?> <object name="example.conference.session" meta_type="Dexterity FTI" i18n:domain="example.conference" xmlns:i18n="http://xml.zope.org/namespaces/i18n"> <!--Basic metadata --> <property name="title" i18n:translate="">Session</property> <property name="description" i18n:translate="">A session on the program</property> <property name="content_icon">document_icon.gif</property> <property name="allow_discussion">True</property> <property name="global_allow">False</property> <property name="filter_content_types">True</property> <property name="allowed_content_types" /> <!-- schema interface --> <property name="schema">example.conference.session.ISession</property> <!-- class used for content items --> <property name="klass">plone.dexterity.content.Item</property> <!-- add permission --> <property name="add_permission">cmf.AddPortalContent</property> <!-- enabled behaviors --> <property name="behaviors"> <element value="plone.app.content.interfaces.INameFromTitle" /> </property> <!-- View information --> <property name="default_view">view</property> <property name="default_view_fallback">False</property> <property name="view_methods"> <element value="view"/> </property> <!-- Method aliases --> <alias from="(Default)" to="(dynamic view)"/> <alias from="edit" to="@@edit"/> <alias from="sharing" to="@@sharing"/> <alias from="view" to="(selected layout)"/> <!-- Actions --> <action title="View" action_id="view" category="object" condition_expr="" url_expr="string:${object_url}" visible="True"> <permission value="View"/> </action> <action title="Edit" action_id="edit" category="object" condition_expr="" url_expr="string:${object_url}/edit" visible="True"> <permission value="Modify portal content"/> </action> </object>[align=left][font=宋体]再次,这是一个项目。在此,我们已经设置[/font]global_allow[font=宋体]为[/font]False[font=宋体],因此这些对象应该仅仅在[/font]Program[font=宋体]里面才是可添加的。[/font][/align][align=left][font=宋体]在[/font]example.conference.program.xml[font=宋体]里面,[/font]Program[font=宋体]看起来像这样:[/font][/align]<?xml version="1.0"?> <object name="example.conference.program" meta_type="Dexterity FTI" i18n:domain="example.conference" xmlns:i18n="http://xml.zope.org/namespaces/i18n"> <!-- Basic metadata --> <property name="title" i18n:translate="">Program</property> <property name="description" i18n:translate="">A conference program</property> <property name="content_icon">folder_icon.gif</property> <property name="allow_discussion">True</property> <property name="global_allow">True</property> <property name="filter_content_types">True</property> <property name="allowed_content_types"> <element value="example.conference.session" /> </property> <!-- schema interface --> <property name="schema">example.conference.program.IProgram</property> <!-- class used for content items --> <property name="klass">plone.dexterity.content.Container</property> <!-- add permission --> <property name="add_permission">cmf.AddPortalContent</property> <!-- enabled behaviors --> <property name="behaviors"> <element value="plone.app.content.interfaces.INameFromTitle" /> </property> <!-- View information --> <property name="default_view">view</property> <property name="default_view_fallback">False</property> <property name="view_methods"> <element value="view"/> </property> <!-- Method aliases --> <alias from="(Default)" to="(dynamic view)"/> <alias from="edit" to="@@edit"/> <alias from="sharing" to="@@sharing"/> <alias from="view" to="(selected layout)"/> <!-- Actions --> <action title="View" action_id="view" category="object" condition_expr="" url_expr="string:${object_url}" visible="True"> <permission value="View"/> </action> <action title="Edit" action_id="edit" category="object" condition_expr="" url_expr="string:${object_url}/edit" visible="True"> <permission value="Modify portal content"/> </action> </object>[align=left][font=宋体]这个区别在这里是我们使用[/font]Container[font=宋体]类,而且我们过滤可包含的类型([/font]filter_content_types[font=宋体]和[/font]allowed_content_types[font=宋体])来允许仅仅[/font]Sessions[font=宋体]被添加到这个文件夹。[/font][/align]
[align=left]4.3 测试schema驱动类型[/align][align=left][font=宋体]怎么启动[/font]Plone[font=宋体]和测试这个类型,和一些故障解决技巧。[/font][/align][align=left][font=宋体]每个类型都有一个[/font]schema[font=宋体]和[/font]FTI[font=宋体],我们的[/font]GenericSetup[font=宋体]属性注册在[/font]configure.zcml[font=宋体]里面,我们应该能够测试我们的类型。保证你已经运行了一个[/font]buildout[font=宋体],然后像平常一样启动[/font]./bin/instance fg[font=宋体]。添加一个[/font]Plone[font=宋体]站点,然后到[/font]ZMI[font=宋体]中的[/font]portal_quickinstaller[font=宋体]。你应该在那看到你的包,同时应该可以安装它。[/font][/align][font=宋体]一旦安装完成后,你应该能够添加新的内容类型的对象。[/font][align=left][font=宋体]如果[/font]Zope[font=宋体]没有启动:[/font]
[/align]
  • 查找控制台上的错误信息,同时保证你在前端运行着./bin/instance fg。你会有一个语法错误或者一个ZCML错误。
[align=left][font=宋体]如果你没有在[/font]portal_quickinstaller[font=宋体]里面看到你的包。[/font][/align]
  • 确保这些包是被mr.developer取出,或者你在buildout.cfg中有一个develop行作为一个扩展的egg来装载它。develop= src/*应该够了,但是你也可以很明确的添加包,例如使用develop = src/example.conference
  • 确保这个包确实是作为一个egg家在的。它应该在egg部分中的[instance]里面被引用。你可以通过查看生成的bin/instance scriptbin\instance-script.py在windows中)检查这个包在buildout里面是否正确的配置了。在文件的顶部的eggs列表中应该有一行对应你的包的。
  • 确保这个包的ZCML被装载了。你可以通过安装一个ZCML部件(通过buildout.cfg中的[instance]部分中的zcml选项)或者添加一个<include/>行在另外一个包的configure.zcml来装载ZCML。然而,这最简单的方法在Plone3.3及以上版本中是添加z3c.autoinclude.plugin接入点到setup.py中。
  • 确保你已经添加一个<genericsetup:registerProfile/>部分到configure.zcml里面。
[align=left][font=宋体]如果这个包在[/font]portal_quickinstaller[font=宋体]中安装失败:[/font][/align]
  • 在Plone站点的根目录的error_log中查找错误,在你的控制台,或者在你的日志文件中。
  • 检查属性文件的语法和位置。记住你需要一个types.xml列出你的类型,和相应的文件在types/*.xml
[align=left][font=宋体]如果你的形式看起来不对(例如你缺少定制的部件):[/font]
[/align]
  • 确保你的schema源于form.Schema。
  • 记住这些指示需要你指定正确的字段名称,即使他们放置在相关字段的前面或者后面。
  • 检查在Configure.zcml里面有一行<grok:grokpackage="." />
限于自己能力有限,译文中会有一些错误,欢迎指正。
英文原版
<<< [size=3]Dexterity开发手册:第三章开发环境搭建[/size]
[align=right]Dexterity开发手册:第五章>>>
[/align]



设置