分享
 
 
 

最小的Zope编程How-to

王朝other·作者佚名  2008-05-18
窄屏简体版  字體: |||超大  

最小的Zope编程 How-to

作者:maxm 最后修改时间:2001/08/12 翻译:limodou 翻译时间:2001/10/25

>>原文链接:http://www.zope.org/Members/maxm/HowTo/minimal_01/

Zope产品的编程享有困难的名声。这不是真的。我相信这只不过是因为所演示的例子造成的,就象令人讨厌的类一样,展示了太多的特性的缘故。而且这些例子在解释功能的时候还跳过了几个步骤,使得“Zope”的道路看上去很长并令人厌烦。所有不必要的额外的代码“只是因为要加入”而被加进去了。

我还记得第一次看到这些令人讨厌的例子的时候,我想这些都是关于Zope的难懂的巫术,不会有人真正的理解,除非在别的地方看到过。

因此我决定写一系列的How-to,用来解释以最简单的方法来进行Zope编程。这一过程将会非常非常缓慢,并且每一行代码将被详细地解释。如果有任何东西你不太清楚,请把它告诉我。

这一系列的第二部分可以在这里找到。

一个非常简单的类

作为开始,我将创建一个可以想到的最简单的Python类,然后演示都需要向它加些什么东西,用来生成Zope的产品(product)。在那之后,我将演示另一个例子,它将这个简单产品进行扩展用于其它方面。

我的最小的“Hello world”类看上去象这样:

class minimal:

def index_html(self):

return '<html><body>Hello World</body></html>'

到了这里,它可以在Python中运行,但在Zope中不行。有几个东西需要增加或进行修改。第一件事情就是要知道Zope不会公开(publish)不带文档字符串(doc string)的任何东西。所以需要把它加进去:

class minimal:

"minimal object"

def index_html(self):

return '<html><body>Hello World</body></html>'

Zope进行对象公开。意味着一个在Python对象中的方法可以被看成web服务器上的一个页面。这就是说你最终可以将上面的类的寻址写成象这样:

http://localhost:8080/minimal_id/index_html

会得到“Hello World”的结果。但是你可以看到,这个对象有了一个叫做“minimal_id”的寻址。它是从哪来的?嗯没什么地方。但是应该很明显,任何一个对象需要一个id,以便Zope能够对它寻址。所以我需要向对象增加一个id属性。我将重复叙述“任何Zope产品必需拥有一个叫做id的属性。否则Zope没有办法去调用它,你将没有办法给它一个URL”:

class minimal:

"minimal object"

def __init__(self, id):

"initialise a new instance of Minimal"

self.id = id # And this little fella is NOT optional

def index_html(self):

"used to view content of the object"

return '<html><body>Hello World</body></html>'

编写和使用一个Zope产品有三个步骤:

1 所有的Zope产品需要被构造为Python的包。放在“lib/python/products/"中的一个文件夹中。Zope将这个目录下的所有的包看成为产品,并且试着在启动时安装它们。这个包在某些阶段必需使用Zope API以便可以通过Zope进行发布。

2 如果包创建的正确,它将在Zope管理视图中的产品列表中显示出来。

3 它现在可以被安装了。意味着你可以将这个产品的一个拷贝放到任何你喜欢的文件夹中去。只要进入一个文件夹,从选择框中选择这个产品就可以了 (译注:这里已经是在说从Zope的内容管理界面中增加一个产品,而不是产品的安装)。这就是Zope真正的力量。如果你生成了一个产品,它就能够在你的或其它某个人的站点的任何地方被容易地重用。你可以把它放到Zope.org上,让其它人在他们的站点上使用它。这个真是太-太-太-伟大了,恕我直言,这就是使用Zope的最真正的原因。

哦,另外还有一点东西需要加入,就是允许向一个文件夹添加产品。需要在选择框中有一个名字才行。这就叫做"元类型(meta_type)",它应该是给你的产品起的唯一识别的名字。所以不要把它叫成象“dtml-document”或“Folder”或类似的东西。

我把这个产品的元类型定为“minimal”:

class minimal:

"minimal object"

meta_type = 'minimal' # this is the same for all

# instances of this class

# so it's declared here and

# not in "__init__()"

def __init__(self, id):

"initialise a new instance of Minimal"

self.id = id # And this little fella is NOT optional

def index_html(self):

"used to view content of the object"

return '<html><body>Hello World</body></html>'

为了同Zope一起良好的运行,我的类得需要一些基本的功能。通常通过对一个叫做“SimpleItem.Item”的类进行子类化来实现。

从Zope书中引用的原话为“这个基类向你提供能够同Zope管理界面一同工作所需要的基础。通过从Item进行继承,你的产品类可以获得一大堆的特性:剪切和粘贴的能力,具有管理视图的能力,WebDAV支持,基本的FTP支持,撤消支持,所有权支持,和遍历(traversal)控制。同时它也向你提供了一些标准的方法用于管理视图和包括了manage_main()的错误显示。你也可以得到getId(),title_or_id(), title_and_id()方法和this()的DTML应用方法。最后这个类向你的产品提供基本的对dtml-tree标记的支持。Item真是一个提供了除厨房和水池外一切东西的基类。”

但实际上我可以做的比它更出色。有另一个叫做“SimpleItem.SimpleItem”的类,它可以实现所以上面的功能,而且更多。“SimpleItem.SimpleItem”还提供了获取(aquisition)和持续(persistence)的功能。

这个类位于OFS的Zope包中,所以它必需被导入。然后我修改了类的声明以便“minimal”类可以对其子类化(译注:用派生可能更清楚):

from OFS import SimpleItem

class minimal(SimpleItem.SimpleItem):

"minimal object"

meta_type = 'minimal' # this is the same for all

# instances of this class

# so it's declared here and

# not in "__init__()"

def __init__(self, id):

"initialise a new instance of Minimal"

self.id = id # And this little fella is NOT optional

def index_html(self):

"used to view content of the object"

return '<html><body>Hello World</body></html>'

技巧!这面是Zope在使用中的技巧之一。Python可以一次从几个基类进行继承。一些类提供了某些种类的功能,别一些则提供了另一些。这种混合类的编码风格叫作“mixins”[1],它强大但理解困难。小心一点!

现在我有了一个完整的类。我仅需要一个可以将对象放入一个文件夹的方法。这可以通过名为“_setObject()”的标准Zope方法来实现。

_setObject是一个在ObjectManager类中的方法。这一部分要花一点心思去理解,因为_setObject()不是做为minimal的方法,而是作为ObjectManager的方法(minimal类将被加入其中)来调用的。

如果我试着向一个叫做myFolder的文件夹添加一个minimal类,我象这样调用:

myFolder._setObject('minimal', minimal('minimal_id'))

仅有“_setObject”是不可能将一个你的对象实例插入到一个文件夹中去的。必需要调用它,并给出一些参数。所以我需要一个新的方法来做这件事。我叫它为“manage_addMinimal()”方法。

from OFS import SimpleItem

class minimal(SimpleItem.SimpleItem):

"minimal object"

meta_type = 'minimal' # this is the same for all

# instances of this class

# so it's declared here and

# not in "__init__()"

def __init__(self, id):

"initialise a new instance of Minimal"

self.id = id # And this little fella is NOT optional

def index_html(self):

"used to view content of the object"

return '<html><body>Hello World</body></html>'

def manage_addMinimal(self):

"Add a Minimal to a folder."

self._setObject('minimal_id', minimal('minimal_id'))

从函数的作用范围可以清楚的看到,manage_addMinimal()函数是从试图向自身添加minimal产品的文件夹中调用的。所以换句话说,manage_addMinimal()方法不是属于“minimal”类的方法,而是属于“ObjectManager”类的方法。

"self._setObject('minimal_id', minimal('minimal_id'))"

传入的第一个参数是id。这里我叫它为minimal_id。所以id是对这个对象的硬连接,应该总是minimal_id。自然这样不太好,因为在一个文件夹中只能有一个实例。但,嗨 ... 这是最小的 ... 记得吗?

然而最后一个方法存在一个问题。当调用时,它不返回任何值。所以当这个方法被调用时,浏览器会显示些什么呢??你猜一猜。什么都没有!或者当你已经放开选择框来增加实例时,只是看上去什么都没有发生。真扫兴。很自然我应增加某种返回页面,但是这对于一个最小产品来说太麻烦了。我将只是让浏览器重定向到index_html。

“redirect()”方法存在于RESPONSE对象中,并且每次Zope调用一个方法时,RESPONSE都作为一个参数传给方法。所以很容易得到它。它应该正好被加入到方法的参数列表中。(这种用法与.asp的方法相比不同之处为在.asp中“Response”是一个全局对象。)

但不管怎么样,类现在完成了,并且看上去象这样:

from OFS import SimpleItem

class minimal(SimpleItem.SimpleItem):

"minimal object"

meta_type = 'minimal' # this is the same for all

# instances of this class

# so it's declared here and

# not in "__init__()"

def __init__(self, id):

"initialise a new instance of Minimal"

self.id = id # And this little fella is NOT optional

def index_html(self):

"used to view content of the object"

return '<html><body>Hello World</body></html>'

def manage_addMinimal(self):

"Add a Minimal to a folder."

self._setObject('minimal_id', minimal('minimal_id'))

RESPONSE.redirect('index_html')

现在我只需要将它放入一个Python包中,并且把这个包放到我的Zope安装中的正确目录中去即可。

我的包叫做minimal,结构为:

minimal

minimal.py

__init__.py

创建包

现在所要做的是使用某种方法通知Zope这个包是一个Zope产品,并且当某人放开选择框向一个文件夹添加实例时,Zope需要知道调用哪个方法来实现添加。我知道,你也知道是“manage_addMinimal()”,但是Zope不知道。我们使用在包中叫做__init__.py的文件来告诉Zope所有这一切:

import minimal

def initialize(context):

"""Initialize the minimal product.

This makes the object apear in the product list"""

context.registerClass(

minimal.minimal,

constructors = (

minimal.manage_addMinimal, # This is called when

# someone adds the product

)

)

首先我从minimal.py中导入minimal,这么做是因为minimal被用在了“initialize()”方法中。

“initialize()”方法是当启动Zope服务器时由Zope来调用的。这就是为什么我们需要重新启动Zope来安装一个新产品的原因。

Zope传递“context”类给“initialize()”。然后“context.registerClass()”通过将 “minimal”作为第一个传给它的参数在Zope中注册类。第二个参数是从minimal模块来的方法元组(tuple),它们被用作构造器。构造器是这样的方法,它们向文件夹添加实例。并且你应该记得它是“manage_addMinimal()”。记住不要在方法名字后面加小括号。我们不想让方法被执行,只是想告诉Zope是什么方法。这就是将“manage_addMinimal”方法绑定到objectManager上去的地方。

通常使用一个表格来添加产品,但是我不在这里那样做。因为那样不会是最小的。关于这一点后面再详细讲。

这就是一个我能想到的具有全部Zope产品功能的最小的类。当然它仍然十分粗糙,但是它就是编写一个类所需要的全部知识了。这个类可以具有象.asp或.php一样的功能。它没有持续(persistence),没有安全或用户管理。只是象其它那些产品。

但这些只是Zope冰山之一角。实现对象的持续现在相当容易,你不需要额外的数据库来存放数据。当在不同的文件夹中加入产品的同一实例时,这一点特别好。它们会自动保存实例的值,并且使得重用更容易。

Zope可能不十分象.asp或.php一样容易,但我想说它也是“陡峭的学习曲线”(译注:好象是说越往后往简单)。Zope在创建web应用程序时有大量的好处,而其它工具则没有。尽管Zope看上去复杂,这是因为它对其它工具考虑得更多的缘故。

想象一下当使用其它工具为你的某个客户建立一个讨论区的情形吧。你创建应用。到目前为止还很好。但是现在你想要为另一个客户重用它。也许是你的老板要你这么做的。这不是什么好消息,因为你的html和代码结合的很紧密。所以你几乎需要为新的客户重新编写。

突然,有某个人在讨论区中使用了“转贴”。所以你的客户想要能够删除注解。但管理页面应该被隐藏在一个口令之后。所以你创建了一个简单的口令系统,并且在一个会话(session)变量中保存用户名。不错 ... 现在每个人都高兴了。

直至老客户也想要同样的管理功能和口令保护。哦,是的,贴新文件的方式也是口令保护的,但在讨论区中允许贴新贴与删除消息是不一样的,所以某些更高级的用户管理表格需要创建。

哦,顺便提一下用户已经买下了一家在德国的公司,它有着自已的讨论区和新闻工具。

这类事情在做web应用时是很典型的。有时候你将需要Zope已经内置这些功能。但是因为Zope内置了这些功能,这样就比起“原始”工具如“mod_perl”,“.asp”和“.php”看上去难得多。

“minimal”类的两个最终文件

__init__.py:

import minimal

def initialize(context):

"""Initialize the minimal product.

This makes the object apear in the product list"""

context.registerClass(

minimal.minimal,

constructors = (

minimal.manage_addMinimal, # This is called when

# someone adds the product

)

)

minimal.py:

from OFS import SimpleItem

class minimal(SimpleItem.SimpleItem):

"minimal object"

meta_type = 'minimal'

def __init__(self, id):

"initialise a new instance of Minimal"

self.id = id

def index_html(self):

"used to view content of the object"

return '<html><body>Hello World</body></html>'

def manage_addMinimal(self, RESPONSE):

"Add a Minimal to a folder."

self._setObject('minimal_id', minimal('minimal_id'))

RESPONSE.redirect('index_html')

就是这些了。我现在已经编写了一个最小类,它可以被用来开发其它的类。

minimal类的一些简单增强

改善minimal类的最明显的方法是当增加一个实例时能够修改id。为了做到这一点,我需要增加一个表格到minimal,这样当用户放开选择框时,他们就可以输入一个id。

class minimal(SimpleItem.SimpleItem):

"minimal object"

meta_type = 'minimal'

def __init__(self, id):

"initialise a new instance of Minimal"

self.id = id

def index_html(self):

"used to view content of the object"

return '<html><body>Hello World</body></html>'

def manage_addMinimal(self, id, RESPONSE=None):

"Add a Minimal to a folder."

self._setObject(id, minimal(id))

RESPONSE.redirect('index_html')

def manage_addMinimalForm(self):

"The form used to get the instance' id from the user."

return """<html>

<body>

Please type the id of the minimal instance:

<form name="form" action="manage_addMinimal">

<input type="text" name="id">

<input type="submit" value="add">

</form>

</body>

</html>"""

由“manage_addMinimalForm()”返回的html表格在提交后会调用“manage_addMinimal()”。“id”被作为一个参数传递。

但我也需要修改“manage_addMinimal()”,因为它需要使用id。所以将使用固定的“minimal_id”改为现在使用用户在文本框中输入的内容作为一个id。现在在文件夹中可以有任意数量的实例了,只要它们有着不同的id。

我也修改了__init__.py,因为当添加一个实例时,Zope现在需要调用“manage_addMinimalForm()”,而不是“manage_addMinimal()”。

import minimal

def initialize(context):

"""Initialize the minimal product.

This makes the object apear in the product list"""

context.registerClass(

minimal.minimal,

constructors = (

minimal.manage_addMinimalForm, # The first method is

# called when someone

# adds the product

minimal.manage_addMinimal

)

)

最后最后要说的

仍然还有一个小问题。当向一个文件夹中添加产品的一个实例时,我可以看到对象id,如果我在上面点击,我进入到产品的管理屏幕。唯一的问题是我还没有定义这样一个屏幕。所以当进入到minimal_id/manage_workspace时,Zope给我一个错误。它对于实例的工作没有什么影响。只是看上去有些粗糙。通过向类中增加一个“manage_options”的元组可以避免:

manage_options = (

{'label': 'View', 'action': 'index_html'},

)

在管理视图中将会有一个单个的tab页,叫做“view”,并且作为缺省的它将进入index_html。所以当某人点击在文件夹中的实例的链接时,他们将看到“index_html”。这样就没有不确切的尾巴了。

那么最终的类看上去象这样:

class minimal(SimpleItem.SimpleItem):

"minimal object"

meta_type = 'minimal'

manage_options = (

{'label': 'View', 'action': 'index_html'},

)

def __init__(self, id):

"initialise a new instance of Minimal"

self.id = id

def index_html(self):

"used to view content of the object"

return '<html><body>Hello World</body></html>'

def manage_addMinimal(self, id, RESPONSE=None):

"Add a Minimal to a folder."

self._setObject(id, minimal(id))

RESPONSE.redirect('index_html')

def manage_addMinimalForm(self):

"The form used to get the instance' id from the user."

return """<html>

<body>

Please type the id of the minimal instance:

<form name="form" action="manage_addMinimal">

<input type="text" name="id">

<input type="submit" value="add">

</form>

</body>

</html>"""

向产品增加更多的页或方法

我将加入一些额外的东西演示如何向类中加入更多的方法。在这里我已经加入了三个方法“counter”,“squareForm”和“square”。试着理解它们是如何工作的。比起mod_perl,.asp或.php没有太多的区别。

from OFS import SimpleItem

class minimal(SimpleItem.SimpleItem):

"minimal object"

meta_type = 'minimal'

manage_options = (

{'label': 'View', 'action': 'index_html'},

)

def __init__(self, id):

"initialise a new instance of Minimal"

self.id = id

def index_html(self):

"used to view content of the object"

return '<html><body>Hello World</body></html>'

def counter(self):

"Shows the numbers from 1 to 10"

result = '<html><body>Counts from from 0 to 10\n'

for i in range(10):

result = result + str(i) + '\n'

result = result + '</body></html>\n'

return result

def squareForm(self):

"User input form for the suare method"

return """<html>

<body>

Please type the number you want squared:

<form name="form" action="square">

<input type="text" name="value:int">

<input type="submit" value="Square">

</form>

</body>

</html>"""

def square(self, value=0):

"Returns the input value squared"

return """<html><body><b>The result of %s squared is:</b>

%s</body></html>""" % (str(value), str(value*value))

# Administrative pages

def manage_addMinimal(self, id, RESPONSE=None):

"Add a Minimal to a folder."

self._setObject(id, minimal(id))

RESPONSE.redirect('index_html')

def manage_addMinimalForm(self):

"The form used to get the instance' id from the user."

return """<html>

<body>

Please type the id of the minimal instance:

<form name="form" action="manage_addMinimal">

<input type="text" name="id">

<input type="submit" value="add">

</form>

</body>

</html>"""

最后

我希望这个How-To有些帮助。当然在这里我所做的不是Zope通常使用的方法。但是我是想把这个How-To做为一个基本的关于产品创建的介绍,它可以用来解释zope的其它部分,通过从这个例子进行扩展。

--------------------------------------------------------------------------------

[注1] 这里对Mixin的解释好象不对,似乎应该是在运行时对类的基类进行重定义,从而使新生成的类实例具有新的属性和方法。关于这一点,本人有一篇文章,可以看一下。《Mix-in技术介绍》

原文可以去我的主页看,这里的格式不好。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有