添加视图 难易程度: 初学者 技能要求: 已经掌握了“编写一个新的内容组件”一章的内容; 懂一些表示组件的概念(可选)。 问题/任务: 既然我们已经有了两个功能完整的内容对象, 接下来要做的就是尽可能让这些功能对用户可用。不过,现在这儿只有三个非常简单的视图:增加(add),编辑(edit)和内容(contents)。这章我们将为消息和留言簿创建一个非常不错的详细留言界面和子线程视图。 解决方案: 本章的内容将围绕browser-based视图组件展开,视图将是我们在这儿主要讨论的知识,其次就是适配器。 这儿列出了编写视图的几种主要方法,下面说说需要涉及的内容: 我们已经学习了browser:addform、browser: editform和browser:containerViews 指令的使用。Forms真的令人难以置信,它允许您任意程度的定制; browser:page和browser:pages指令是易于创建浏览器视图和成组视图最常用的指令。我们将使用这两个指令创建我们的视图; zope:view是一个低级别的指令,它为注册多个视图(multi-views)提供相关功能,其它指令没有能力来干这些工作。 第I步: 详细消息视图 现在我们开始以创建两个新的浏览器视图为本章的目标。我们现在已经能编辑消息,可当前我们并没有查看该消息的简单视图,这是非常重要的,因为不是很多人都有权利进入编辑页面的。 消息视图应显示的详细资料如下: 标题(title)、作者(author), 创建日期/时间(creation date/time), 父标题(链接到消息)(parent title)、内容(body)。 编写视图通常包括编写页面模板、一些支持Python的视图类、一些ZCML。现在我们将开始创建页面模板。 (a) 创建页面模板 在messageboard的browser中创建名为details.pt的文件,在该文件中填充如下内容: 2 3
4 5

Message Details

6 7
8
Title
9
10
11 12
13
Author
14
15
16 17
18
Date/Time
19
20
21 22
23
Parent
24
25 28
29
30 31
32
Body
33
34
35 36
37 38 ]]> 第1-3行& 36-38行:这是一些针对Zope页面模板的标准样板文件,已经被嵌入到了显示数据中。这样做的好处就在于所有的页面有一致的界面,并且允许开发者专注于视图的功能部分。 第9行:title能直接从内容对象中重新得到,作为context它是可用的。 第14 &19行:作者(author)和修改日期(modification date/time )不是直接可用的,因为它们只是对象元数据(都柏林核心数据集)的一部分。因此我们需要通过基于Python的视图类让它们可用。基于Python的视图类的作用在于它能重新得到和准备数据。 第24-27行:通过相对简单的TALES路径表达式,我们或许能达到父对象,在下一步中您将看到怎样收集这些信息。 (b) 创建基于Python的视图类 从 (a)部分我们可以知道, 在我们的视图类中我们需要以下方法(或属性): author()、 modified()、 parent_info() 首先我们在browser包中创建名为message.py的新文件,注意在这个模块里我们将为IMessage安置所有与browser相关的Python 代码。 下面是我们的执行清单: 第1行:通过zapi模块可以得到您所需要的大多数基础的程序,zapi模块提供了所有至关重要的组件构建方法,比如说getParent(),还有所有可利用的核心servicenames 。参见ZOPE3/src/zope/app/interfaces/zapi.py就可以得到通过zapi模块的一个完整的可利用的方法列表。 第2行:ICMFDublinCore接口被用于存储都柏林核心数据,我们可以使用这个接口得到我们需要得到的信息。 第7行:注意,这个视图类没有基础类或指定任何应用接口,理由是稍后可以通过ZCML指令为视图添加BrowserView基础类。 在Zope3的某些部分您仍然能够看到从BrowserView继承的视图类。 第12-16行:这段代码试着从都柏林核心集获得创建者(涉及到作者)列表。如果创建者没有发现,就返回字符串"unknown",否则列表中的第一个创建者将被返回,也就是这个对象的拥有者或原始作者。注意由于消息没有被编辑过(作为开发步骤),因此通常应该仅仅有一项。 第20-28行:发现修改日期更难对付,由于我们在创建时只创建了字段而非修改字段。因此我们首先尝试获取被修改字段,如果失败我们将获得创建字段,如果创建日期都不存在,我们就返回空字符串。 如果最后发现了日期对象,我们就把它转化成字符串并返回它。 第30-33行:使用getParent()方法我们可以比较轻易的得到父对象。但是我们需要确认父对象仍然是一个Imessage对象。如果不是并且还有一个根消息,我们就返回None。如果是的话这个父对象的名称和标题将被存放在字典中,以至于能够比较轻易的在页面模板中重新得到数据。 (c) 注册视图 最后的工作就是用ZCML注册这个新的视图。在browser目录中打开配置文件(configure.zcml)并添加如下各行: ]]> 第1行:browser:page指令注册一个单独的页面视图。 第2行:name属性指定了这个视图,可通过如下地址访问: name属性是必需的。 第3行:For属性将告诉系统这个视图是针对Imessage对象。如果属性没有被指定,表明视图将注册的接口适合全部对象。 第4-5行:为该视图创建MessageDetails类和details.pt页面模板;details.pt页将作为视图被提供和用做MessageDetails的实例。 注意不是所有的视图都需要一个视图类来支持,因此该类属性是可选的。 通常您为一个规则的页面指定页面模板时,您也许宁愿有视图基于Python视图类属性的情形存在。这样的话,您能指定代替模板的属性。指定的属性/方法应该以unicode字符串的形式返回并最终输出。 第6行:Permission属性指定了查看该页面被要求的权限。现在我们想设置任何该站点的用户都有打开详细页的权限,于是我们设定了zope.Public权限。 第7行:为了避免使用另外单独的菜单条目指令,我们可以利用菜单和名称属性来告知系统,网页应当隶属于哪一个菜单。此处我们使用一个标签(zmi_views menu),并将其命名为Preview(预览)。 现在您必须重新启动Zope,如果您以前没有添加一个消息对象的话请单击添加它。"Preview" tab菜单条目现在应该有效。假如这个消息没有包含在另一个消息里面,请注意界面上将没有"Parent"条目显示。 为了在界面上看到"Parent"条目,需要使用"Contents"视图在当前消息下面新增另一条消息。现在您应该能看到一个能够链接到父消息的"Parent"条目。 (d) 测试视图 在继续下一项之前,我们应该开发一些功能测试来确保视图正确工作。功能测试通常是自动向前执行的,因为它们仿真用户通过UI所采取的步骤。唯一感到棘手的问题是如何正确设置所有的变量。 整个Zope3 系统提出了进行功能测试,以便一个对象的所有副作用和行为在它处的环境里面都可能被测试出来。经常进行一些简单的测试也能在UI和ZCML里发现出致命的错误,这是由于功能测试起动后所有的过程都将被执行。 在下面的功能测试中将确信消息能够增加并且所有的详细信息将被显示在"Preview"中。所有的功能测试都习惯被存放在名叫ftests的子模块中。当我们决定写这些测试时,让我们通过创建目录和增加__init__.py文件来制作测试模块。 现在我们创建一个名为test_message.py的文件并且加上如下测试代码: 0) 34 self.assert_(body.find('Message 1') > 0) 35 self.assert_(body.find('Body') > 0) 36 37 38 def test_suite(): 39 return unittest.TestSuite(( 40 unittest.makeSuite(MessageTest), 41 )) 42 43 if __name__ == '__main__': 44 unittest.main(defaultTest='test_suite') ]]> 第2行:为了简化写基于浏览器的功能测试,BrowserTestCase被用于测试基类。 第6-23行:在我们准备在一个消息上面测试视图之前,我们必须创建一个消息。我们可以用一个底层API函数创建一个消息。 第7-11行:publish()方法被用于发布者发布一个请求。第一个参数是URL(排除服务器和端口)被发布。通常我们也包括指定用户名和密码的basic参数。系统只识别用户名为mgr和密码为mgrpw的zope用户。zope.Manager角色已经授权给该用户,所以该用户可以使用所有的界面。 在form中,您指定通过HTTP表单机制被提交的所有变量的字典。条目的值已经能被Python对象格式化,而且不必要是生硬的unicode字符串。注意添加的视图要求为这个对象添加一个名叫UPDATE_SUBMIT字段。否则它仅仅认为这是表单重引。 第12-14行:添加视图总是返回重定向(HTTP 代码 302)。我们也能够通过查看"Location"HTTP头来查证目的地。 第15-23行:这里我们重复相同的过程;这次添加一个名叫"msg1"的消息到留言簿中。 第25-35行: 在创建消息对象 (第 26 行) 之后,详细视图被请求并且HTML结果存放在body里(27-29行)。 BrowserTestCase比较好的特点之一是checkForBrokenLinks()方法,方法解析HTML寻找本地的URL,然后尝试检查他们是好链接。方法的第二个参数是生成页面的本页网址。这需要正确地决定位置。我们也应该指定一个相同的验证参数,在发布期间,如果用户有访问这个链接页面的权限,链接才可利用。 在测试的最后(33-35行)我们只是在HTML中检查一些预期的信息,这通常是重要的,因为一个不完善的视图在发布过程当中会导致失败。 第 38 行-44行: 按惯例,我们必须有这些通用的样板代码。 现在测试已经开发完毕,我们象单元测试一样来运行它们,但我们不能使用-U选项(仅仅单元测试可以使用),我们现在指定-f选项(仅用于功能测试)。 1 python2.3 test.py -vpf --dir src/book/messageboard 既然以前您已经看过本页,所有的测试应该很容易地通过, 除非您的测试范例有书写问题。一旦测试通过,您就可以继续下一个任务了。 第II步: 指定缺省视图 此时如果您试着用 查看一个消息,您将会获得一个标准的index.html视图。由于缺省视图只是显示出了消息的内容,因此界面就不是那么合乎用户的需求了。 这儿有一个特殊指令用于声明一个缺省的视图,请在您的browser程序包配置文件中添加如下代码: ]]> 第2行:这里我们告诉系统,我们为实现Imessage的组件添加一个缺省视图。 第 3 行:我们用"Preview"界面来做缺省视图。不过您也可以选择任何您喜欢的视图。当然这些视图通常是用来数据显示,而不是用来数据输入的。同时也建议至少对视图做一些限制并且设为缺省,以便于仅拥有少量权限的用户才能查看到这个对象的某些信息。 第 III 步: 线程树型视图 创建一个好的线程树型视图自然是比较难的,因为稍后它能让我们的视图功能得到增强。通过页面模板能够有效的避免重复同一操作。 (a) 主线程页面模板 现在我们开始为thread.html创建一个主视图模板,这个模板名叫thread.pt: 2 3
4 5

Discussion Thread

6 7
8 9
10 11 ]]> 我们几乎可以把所有东西都做成模板文件,不过,如果我们需要这么做的话,稍后还有更多的机会在这里添加更多功能。 第 7 行:现在我们随便做些应用, 我们简单地假定基于Python的视图类有一个能为这个消息或者留言簿产生我们需要的子线程的方法subthread()。 (b) 线程Python视图类 下一步我们必须建立我们自己的Python视图类。我们开始编辑thread.py文件并且插入如下代码: 第 1 行: ViewPageTemplateFile类允许页面模板有Python类的属性和方法,非常便利。 第 2 行: 导入IMessage 接口, 因为我们稍后为对象标识需要它。 第 25 行: 这里有我们承诺的subthread()方法,它只是一个简单知道怎样描绘线程的页面模板。 注意:在进行之前,您可能想先阅读part (c)。 第12-13行:这一个方法为subthread页面模板提供了它工作所有的必需信息。对于每个child它产生一个信息字典。字典中包括url和thread值这些有趣的元素。URL在循环过程每个迭代中被建立。我们也可以用zope.app.traversing框架产生URL,但我想这种方法更为简单些。 首先我们为每个child创建一个Thread实例(view),然后我们要求视图返回到child的subthread,确定了下一层, 当返回后创建更深的层等等。因此thread值将包含一个分支线程的HTML表示。 (c) 子线程页面模板 我们把view类需要的这个模板命名为subthread.pt,模板仅负责用提供的信息创建款套Message children,因此模板是很简单的(因为没包含逻辑): 2
  • 3 Message 1 6
    7
  • 8 ]]> 第1行:UL总是有益于创建threads 或trees。 第2行:由于Thread view类,我们只是需要反复children信息。 第3-5行:确定我们需要显示的消息标题和链接的实际对象。 第6行:为消息插入subthread。 (d) 注册Thread View 象以前一样注册thread视图。 ]]> 您应该已经比较熟悉这页指令了,因此上述代码应该很容易理解。 您也必须为 IMessageBoard 注册相同的视图,以便于您能得到整个messageboard 的完整线程。 (e) 留言簿缺省视图 由于留言簿还没有一个缺省的视图,现在我们把这个线程视图设为默认: ]]> 这当然和我们在之前为Imessage注册缺省视图有些相似。 第IV步: 添加图标 既然我们有一些基于文本的视图,让我们研究为留言簿和消息定制图标,图标也就是在当前情况下我们内容组件上的视图。然而,为了让browser命名空间提供一个名叫icon的更为方便的指令注册图标,我们需要在browser软件包配置文件中为每个内容类型添加如下指令: ]]> 这段代码此时应该不需要说明。替代上面的模板,我们指定一个文件作为视图,这是图象数据而不是 ASCII文本文件。 现在您已经做了所有的设置。重新启动Zope 3,然后看新功能是否按照您的预期在开展工作。 代码在Zope SVN 中可以得到: 练习 在详细的消息界面中显示父消息的作者。扩展parent_info返回信息字典,包括父消息的作者并且用模板显示它。 如果在每条消息标题后面再有回复、修改以及删除链接(可能是图象)并且能让它们正常工作就更好了。注意您应该能够重用许多现在已经存在的代码。