Zope3学习笔记
Abstract
Zope3 是一个全新的Python Web Framework,与zope2有很大的不同,它采用了很多先进的软件工程技术,开发的软件可重用性高,是很有前途的一种Framework。本文记录我在学习Zope3中的一些知识积累。由于现时Zope3的资料很少,而且都是英文的,学习的难度较大,本文难免会有出错之处,请各位网友多多指正。谢谢!
Table of Contents
Zope3采用了全新的开发模式,充分利用了现时先进的软件工程方法,通过接口和组件开发,大大提高了软件产品的灵活性和可重用性。在Zope3开发架构中,引入了大量新的术语。在这里,我们对这些术语作简短的介绍。
-
Interface(接口)
Interface用以定义软件需实现的功能和详细的描述文档。设计好接口后,我们就可以用Zope3提供的pyskel工具自动生成实现类。语法是:
zope@debian:~/lib/python/mytest$pyskel mytest.interfaces.IExample > example.py
mytest是我的模块目录,在运行上面命令前如在该目录下没有__init__.py文件,则需创建一个空的__init__.py文件,使zope3把mytest当作一个模块导入。
-
Components(组件)
Components是一个具有自省接口的对象,接口是一个描述组件如何工作的对象,Components实现接口描述的功能。在Zope3中的组件有以下几种:
-
Content Components(内容组件),它是组件中最简单的一种,用来存放数据和内容,如文档和图像。
-
Factory Components(工厂组件)
就象它的名字一样,Factory Components用以创建其它组件和对象的组件,就像一个组件的生产工厂,它可以是方法或类。
-
View Components(可视组件)
可视组件为其它组件提供一个用户接口,通常用于显示内容组件。ZPT就是一个可视组件,在该组件中尽量不要包含逻辑处理。
-
Adapter Components(适配器组件)
适配器组件位于内容组件和可视组件之间,用以扩展内容组件的功能。例如:你的文档(内容组件)已有一个edit()方法,没有你需要的 mailToSubscribers()的功能。这时,我们不用修改内容组件或可视组件,只要增加一个适配器组件,用该组件实现 mailToSubscribers()功能即可。
-
Utility Components(工具组件)
工具组件只提供一个特定的功能,它不作用于其它组件。与适配器组件比较,我们可以Python语言中的函数和方法作为比喻。工具组件就像Python中函数,是一个独立的功能对象。适配器组件就像Python中的方法,需在其它对象的基础上才能工作。
-
Service Components(服务组件)
服务组件是由Zope3提供的一组核心功能,它已存在于Zope3中,如:user sessions, authentication, workflow, content-type management等。
Components 还分Global Components和Local Components两类,前者不保存在Zodb中,它保存在系统文件中(zope_instance/lib/python/xxx),在服务器重启时通过ZCML注册和初始化,它与位置路径无关,可在全局范围内访问。后者保存在Zodb中,与定义时的位置相关。一个站点就是一个Local Components,系统有一个默认的站点叫top。我们可通过在ZMI中点击“Manage Site”创建一个Local Components。任何一个文件夹都可通过“Manage Site”提升为一个站点。
-
-
Metadata(元数据)
元数据的意思是表示数据属性的数据,如对象标题、对象创建者和对象创建日期等。我们也可把它理解成关系数据库中表的字段。Zope3使用Dublin Core来表示内容对象的元数据。
-
Subscriber
-
Container(容器)
Container用于存放不同对象,文件夹就是一个典型的Container。Container提供了一种机制处理对象间的包含关系。在Zope3中,Container位于zope.app.container类中,它有三个基类,分别是:
-
BTreeContainer,最通用到的类,可存放大量的对象。
-
SampleContainer,很少用,它为代层的存储提供一个hook。它是BTreeContainer的父类。
-
OrderedContainer,存放排序对象,也不可存放大量对象。
-
-
Unittest(单元测试)
-
ZCML(Zope Configuration Markup Language)
Zope3通过ZCML配置文件把不同的组件组合成一个应用程序,它是一个符合XML标准的文档,配置文件的文件名为configure.zcml。还有一个名为meta.zcml的配置文件用以扩展ZCML配置文件里的XML标签。
-
i18n domains
在ZCML 中可以定义产品的i18 domains,配合Zope3的I18n工具(i18nextract)和gettext工具可以实现产品的国际化。Global Components的i18n信息保存在系统文件中(zope_instance/lib/python/xxx/locals/< REGION>/LC_MESSAGES/xxx.po),Local Components产品的i18n信息保存在Zodb中,它通过ZMI中的translation domain功能创建。
下面是一个公告板的示例程序,这里将一步步展示如何在Zope3中实现它。该程序作为一个Python Package保存在文件系统的,而不是存放在Zodb中,也就是说组成该应用程序的Components是Global Components。Zope3还有一种开发方式叫TTW(Throught-The-Web),它可通过ZMI直接在浏览器中开发。用TTW开发的 Components属于Local Components,保存在Zodb中。
-
准备
开发的Python软件包存放在zope_instance/lib/python目录下,我们首先为该程序创建一个目录叫mymessage:
zope@debian:~/lib/python$ mkdir message
为了使该目录成为一个Python软件包,还需在该目录下创建一个空的__init__.py文件。
zope@debian:~/lib/python/mymessage$ echo "# Make it a Python package" >> __init__.py
-
设计
该程序将有一个根对象叫MessageBoard,它可容纳用户发布的信息和其它用户的反馈信息。另一个对象组件是Message,保存每条信息的内容。
-
创建接口
编码过程的第一步就是定义我们的接口,我们的接口保存在名为interfaces.py的文件中。
from zope.interface import Interface from zope.schema import Text,TextLine,Field from zope.app.container.constraints import ContainerTypesConstraint from zope.app.container.constraints import ItemTypePrecondition from zope.app.container.interfaces import IContained,IContainer from zope.app.file.interfaces import IFile class IMessage(Interface): """A message object. It can contain its own responses.""" title = TextLine( title = u'Title/Subject', description = u'Title and/or subject of the message.', default = u'', required = True) body = Text( title = u'Message Body', description = u'This is the actual message. Type whatever you wish.', default = u'', required = False) class IMessageBoard(IContainer): """The Message board is the base object for our package.It can only contain IMessage objects.""" def __setitem__(name,object): """Add a IMessage object.""" __setitem__.precondition = ItemTypePrecondition(IMessage) description = Text( title = u"Description", description=u"A detailed description of the content of the board.", default = u"", required = False) class IMessageContained(IContained): """Interface that specifies the type of objects that can contain messages.""" __parent__ = Field( constraint = ContainerTypesConstraint(IMessageBoard,IMessage)) class IMessageContainer(IContainer): """We also want to make the message object a container that can contain responses(other messages) and attachments(files and images).""" def __setitem__(name,object): """Add a IMessage object.""" __setitem__.precondition = ItemTypePrecondition(IMessage,IFile) -
实现内容组件
根据接口,我们要实现些什么组件呢?我们可借助pyskel.py这个工具来自动生成。该工具位于zope_instance/bin目录下。
zope@debian:~/bin$./pyskel mymessage.interfaces.IMessage from zope.interface import implements from mymessage.interfaces import IMessage class Message: __doc__ = IMessage.__doc__ implements(IMessage) # See mymessage.interfaces.IMessage body = None # See mymessage.interfaces.IMessage title = None zope@debian:~/bin$./pyskel mymessage.interfaces.IMessageBoard from zope.interface import implements from mymessage.interfaces import IMessageBoard class MessageBoard: __doc__ = IMessageBoard.__doc__ implements(IMessageBoard) def __setitem__(self, name, object): "See mymessage.interfaces.IMessageBoard" # See mymessage.interfaces.IMessageBoard description = None def __getitem__(self, key): "See zope.interface.common.mapping.IItemMapping" def get(self, key, default=None): "See zope.interface.common.mapping.IReadMapping" def __contains__(self, key): "See zope.interface.common.mapping.IReadMapping" def __getitem__(self, key): "See zope.interface.common.mapping.IItemMapping" def keys(self): "See zope.interface.common.mapping.IEnumerableMapping" def __iter__(self): "See zope.interface.common.mapping.IEnumerableMapping" def values(self): "See zope.interface.common.mapping.IEnumerableMapping" def items(self): "See zope.interface.common.mapping.IEnumerableMapping" def __len__(self): "See zope.interface.common.mapping.IEnumerableMapping" def get(self, key, default=None): "See zope.interface.common.mapping.IReadMapping" def __contains__(self, key): "See zope.interface.common.mapping.IReadMapping" def __getitem__(self, key): "See zope.interface.common.mapping.IItemMapping" def __setitem__(self, name, object): "See zope.app.container.interfaces.IWriteContainer" def __delitem__(self, name): "See zope.app.container.interfaces.IWriteContainer" zope@debian:~/bin$上面的结果已很明白的了,但我们可以通过继承BTreeContainer类来简化实现过程。继承了BTreeContainer类后,我们就不用去实现IReadMapping、IEnumerableMapping、IReadMapping、IItemMapping和 IWriteContainer接口的方法了。新创建一个叫messageboard.py的文件,实现IMessageBoard接口,内容如下:
from zope.interface import implements from zope.app.container.btree import BTreeContainer from mymessage.interfaces import IMessageBoard class MessageBoard(BTreeContainer): __doc__ = IMessageBoard.__doc__ """test >>> from zope.interface.verify import verifyClass >>> verifyClass(IMessageBoard,MessageBoard) True """ implements(IMessageBoard) # See message.interfaces.IMessageBoard description = u""新创建一个message.py文件,实现IMessage、IMessageContained和IMessageContainer接口,内容如下:
from zope.interface import implements from mymessage.interfaces import IMessage from mymessage.interfaces import IMessageContained,IMessageContainer from zope.app.container.btree import BTreeContainer class Message(BTreeContainer): __doc__ = IMessage.__doc__ implements(IMessage,IMessageContained,IMessageContainer) # See message.interfaces.IMessage title = u'' # See message.interfaces.IMessage body = u'' -
注册内容组件
到此为止,我们已开发出了我们的组件,现在我们要告诉Zope3如何组装这些组件。这里要用到Zope的配置语言(ZCML),它采用XML语法格式。配置文件名为configure.zcml。
<configure xmlns="http://namespaces.zope.org/zope"> <interface interface = ".interfaces.IMessageBoard" type = "zope.app.content.interfaces.IContentType" /> <content class=".messageboard.MessageBoard"> <implements interface = "zope.app.annotation.interfaces.IAttributeAnnotatable" /> <implements interface = "zope.app.container.interfaces.IContentContainer" /> <factory id = "mymessage.messageboard.MessageBoard" description = "Message Board" /> <require permission = "zope.ManageContent" interface = ".interfaces.IMessageBoard" /> <require permission = "zope.ManageContent" set_schema = ".interfaces.IMessageBoard" /> </content> <interface interface = ".interfaces.IMessage" type = "zope.app.content.interfaces.IContentType" /> <content class=".message.Message"> <implements interface = "zope.app.annotation.interfaces.IAttributeAnnotatable" /> <implements interface = "zope.app.container.interfaces.IContentContainer" /> <require permission = "zope.ManageContent" interface = ".interfaces.IMessage" /> <require permission = "zope.ManageContent" interface = ".interfaces.IMessageContainer" /> <require permission = "zope.ManageContent" set_schema = ".interfaces.IMessage" /> </content> <include package=".browser" /> </configure> -
配置基本视图
注册组件的动作只是进行一些逻辑处理,并不会产生任何的界面。这里我们可通过ZCML语言配置一个可通过浏览器访问的可视界面。我们在mymessage目录下创建一个browser目录,用以存放可视界面相关的内容。同样,为了使该目录成为一个Python Package。我们要在该目录下创建一个空白的__init__.py文件。接着创建一个configure.zcml配置文件,内容如下:
<configure xmlns="http://namespaces.zope.org/browser"> 定义browser名称空间 <addform 定义添加对象的form label = "Add Message Board" name = "AddMessageBoard.html" schema = "mymessage.interfaces.IMessageBoard" content_factory = "mymessage.messageboard.MessageBoard" fields = "description" permission = "zope.ManageContent" 定义访问权限 /> <addMenuItem 在对象添加列表上添加一个Message Board项 class = "mymessage.messageboard.MessageBoard" title = "Message Board" description = "A Message Board" permission = "zope.ManageContent" view = "AddMessageBoard.html" 与addform中的name属性值对应 /> <editform schema = "mymessage.interfaces.IMessageBoard" for = "mymessage.interfaces.IMessageBoard" label = "Change Message Board" name = "edit.html" permission = "zope.ManageContent" menu = "zmi_views" title = "Edit" /> <containerViews for = "mymessage.interfaces.IMessageBoard" index = "zope.View" contents = "zope.View" add = "zope.ManageContent" /> <addform label = "Add Message" name = "AddMessage.html" schema = "mymessage.interfaces.IMessage" content_factory = "mymessage.message.Message" fields = "title body" permission = "zope.ManageContent" /> <addMenuItem class = "mymessage.message.Message" title = "Message" description = "A Message" permission = "zope.ManageContent" view = "AddMessage.html" /> <editform schema = "mymessage.interfaces.IMessage" for = "mymessage.interfaces.IMessage" label = "Change Message" fields = "title body" name = "edit.html" permission = "zope.ManageContent" menu = "zmi_views" title = "Edit" /> <containerViews for = "mymessage.interfaces.IMessage" index = "zope.View" contents = "zope.View" add = "zope.ManageContent" /> </configure>为了使该配置文件生效,我们要在mymessage目录下的主configure.zcml文件中包含该文件。具体做法是在主configure.zcml文件的<configure>标签内包含以下语句:
<include package=".browser" />
-
测试
为了使该对象生效,我们要重启服务器。登录进Zope3后,我们可以看到,在右边的对象添加列表中多了一个MessageBoard项目,点击即可添加。在添加的MessageBoard对象内,我们可以添加Message对象,而且只能添加 Message对象。点击打开Message对象,我们还可以再添加Message对象(回复)或附件(一般文件或图片等)。
这里我们看到的界面是Zope3标准的格式,可能不是很符合你的要求,我们也可用可视组件(ZPT)重新设计一个界面,增强程序的表现力。
Table of Contents
现时Zope3的学习资料很少,网上的资料都比较零散。这个HowTo记录我接触Zope3时的一些积累。
zwiki是在zope上的一个wiki系统,它在zope2平台上取得很大的成功。zope3平台现时的软件包还很少,官方网站上只有几个。下面介绍一个在zope3平台上安装zwiki的过程。
-
把下载的软件包解压,生成zwiki-3.0.0目录,进入该目录并把该目录下的zwiki目录拷贝到zope3实例目录下的lib/python目录。
-
zope3利用zcml文件来注册产品,所以我们要把zwiki-3.0.0/zwiki/zwiki-configure.zcml产品配置文件拷贝到zope3实例目录下的etc/package-includes目录中。
-
重启zope3服务器。登录http://localhost:8080即可在对象添加列表中看到多了Wiki这个对象,点击即可添加使用。
这里收集了我在使用Zope3中遇到一些小问题和我的解决方法。
-
问题:用./runzope启动服务器时,提示“LookupError: unknown encoding: gb2312”出错信息。
原因:我的python版本是python2.3.5,不支持gb2312编码。下面是我的测试过程:
Python 2.3.5 (#2, Sep 4 2005, 22:01:42) [GCC 3.3.5 (Debian 1:3.3.5-13)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> a="中国" >>> print a 中国 >>> a.decode('gb2312') Traceback (most recent call last): File "<stdin>", line 1, in ? LookupError: unknown encoding: gb2312解决方法1:把python版本升级到python2.4。升级后我进行了验证:
Python 2.4.1 (#2, May 5 2005, 11:32:06) [GCC 3.3.5 (Debian 1:3.3.5-12)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> a="中国" >>> print a 中国 >>> a.decode('gb2312') u'\u4e2d\u56fd'解决方法2:为Python2.3.5增加gb2312编码支持。具体的操作方法可参考http://www.linuxmine.com/2915.html。
文章整理:iocblog
版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。