Zope 3 en 30 minutes
Une courte introduction à Zope , le texte développe une application minimaliste de marque pages et donne un mot d'explication sur les concepts clé ensuite. Le but du document est d'introduire Zope 3 à un nouvel utilisateur.
Ce texte est une traduction libre du texte "Zope3 in 30 Minutes" par Baiju M. Le code a été modifié pour fonctionner sur Zope 3.3.0 sans erreurs (l'auteur utilise Zope 3.1 dans le texte original). L'originial est disponible sur : http://zissue.berlios.de/z3/Zope3In30Minutes.html et http://svn.berlios.de/viewcvs/zissue/trunk/z3in30m.
| Author: | Baiju M <baiju.m.mail AT gmail.com> |
| Version: | 0.1.96 |
| Copyright: | (C) 2005 Baiju M, Placed under GNU GPL version 2, or (at your option) any later version |
| Source: | http://svn.berlios.de/viewcvs/zissue/trunk/z3in30m |
| Traduction: | Benjamin Poulain < ikipou AT (@) gmail.com > |
Démarage en vitesse
Pourquoi 30 minutes ?
Désolé, Zope 3 ne peut pas être introduit en 10 minutes :)
Et pourquoi devrais-je ... ?
Si vous cherchez un framework Pythonic pour le développement d'application web, continuez à lire. Et je ne vais pas faire une nouvelle définition de "Qu'est ce que Pythonic?" :) Ici, je vais essayer de répondre à votre question "pourquoi?".
Mots clés
Python, Zope, Interface, Composants, ZCML, ZMI, Adapter, View, ZPT, Evénement, Outil, Principal, ZODB, ZEO
Commençons
Zope 3 est la troisième génération de Zope, un framework pour application web. Vous pouvez télécharger Zope 3.3 sur la page de produit de Zope : http://www.zope.org/Products/Zope3. Pour installer, décompressez le paquet de source de Zope et tapez ceci en root :
# cd Zope-3.3.0
# ./configure;make;make install
Vous devez avoir le paquage python-dev installé pour la compilation. Après l'installation, vous devez créer une instance (ne vous inquietez pas, faîte le) :
$ cd /usr/local/Zope-3.3.0/bin
$ ./mkzopeinstance --dir=$HOME/myzope --user=admin:secret123
Pour lancer le serveur Zope 3, allez dans le dossier de votre instance et :
$ cd ~/myzope
$ ./bin/runzope
Si vous obtenez une erreur de port, verifiez que le port 8080 n'est pas utilisé par un autre programme. Lancez votre navigateur, ouvrez http://localhost:8080; Ce que vous voyez est l'interface de management de Zope (ZMI). ZMI est votre prompt Python, hmm... non! votre prompt Zope. Vous pouvez vous loguer et regarder un peu ce qu'il est possible de faire. Quand vous aurez joué assez avec ZMI, arrêtez le du terminal (Control + C).
Marque-pages : Votre première application Zope 3
Oui!, nous allons créer une application Zope 3, un système de marque-pages online Notre application va montrer des liens vers des sites web et une description poru chaque lien.
Alors, a quoi devez vous penser pour commencer un projet Zope 3 ? Oh! désolé!, je peux mettre tout ça en une phrase, vous pouvez apprendre et utiliser l'Extreme Programming.
Quoi qu'il en soit, après votre design, vous allez écrire des interfaces. Après vous pourrez écrire les tests unitaire, mainteant vos idées seront très concrète. Finalement, vous écrirrez votre code. Vous verrez la satisfaction d'implémenter les interfaces une par une et de voir les tests unitaires réussir. Je vous donne le code de l'application marque page ici : boom.tar.bz2(TODO + 8859-15).
Notre code sera placé dans $HOME/myzope/lib/python/boom. N'oubliez pas d'y créer un fichier __init__.py pour que ce repertoire boom soit un paquage Python valide.
Pour commencer créer un fichier nommé interfaces.py où nous allons définir nos interfaces. Après nous implémenterons ces interfaces une par une, avec le support des tests unitaires.
Interfaces
Voici notre fichier interfaces.py (boom/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
class IMark(Interface):
"""Ceci est l'objet marque page"""
url = TextLine(
title=u"URL/Link",
description=u"URL du site web",
default=u"http://www.zope.org",
required=True)
description = Text(
title=u"Description",
description=u"Description du site web",
default=u"",
required=False)
class IBookMarker(IContainer):
"""Ceci est le container/gestionnaire des marques pages"""
name = TextLine(
title=u"Nom du gestionnaire",
description=u"Un nom pour le gestionnaire de marque pages",
default=u"",
required=True)
def __setitem__(name, obj):
pass
__setitem__.precondition = ItemTypePrecondition(IMark)
class IMarkContained(IContained):
"""Une contrainte pour ne pouvoir ajouter les marque pages qu'au gestionnaire"""
__parent__ = Field(
constraint = ContainerTypesConstraint(IBookMarker))
Notre première interface IMark a deux attributs, un pour l'URL du site et l'autre pour une déscription facultative. Notez que IMark n'est pas une classe même si on a utilisé la définition de classe de Python. Nous héritons de Interface (zope.interface.Interface) ce qui fait de la définition une interface.
La seconde est une interface container, qui étend l'interface IContainer. Nous allons mettre tout les object de type IMark dans un object container de type IBookMarker. Nous allons implémenter IMark avec l'interface IMarkContained comme contrainte de façons à ce que les les objects de IMark ne pourons être contenu que dans des objects IBookMarker.
Test unitaires
Maintenant créez le fichier tests.py et mettez y le code suivant (boom/tests.py) :
import unittest
from zope.testing.doctestunit import DocTestSuite
from zope.app.container.tests.test_icontainer import TestSampleContainer
from boom.bookmarker import BookMarker
class BookMarkerContainerTest(TestSampleContainer):
def makeBookMarkerObject(self):
return BookMarker()
def test_suite():
return unittest.TestSuite((
DocTestSuite('boom.bookmarker'),
unittest.makeSuite(BookMarkerContainerTest),
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
Pour le moment ce code ne réalise aucun test, mais celà va nous permettre de lancer les doc tests automatiquement.
Pour lancer les tests unitaire :
$ cd ~/myzope/lib
$ ../bin/test -vpu --dir boom
Vrai codage !
Maintenant attaquons nous à l'implémentation (boom/bookmarker.py) :
__docformat__ = 'restructuredtext'
from zope.interface import implements
from zope.app.container.btree import BTreeContainer
from zope.app.container.contained import Contained
from boom.interfaces import IMark, IMarkContained, IBookMarker
class Mark(Contained):
"""Implémentation de IMark
Assurons nous que `Mark` implémente l'interface `IMark`::
>>> from zope.interface.verify import verifyClass
>>> verifyClass(IMark, Mark)
True
Assurons nous que `Mark` implémente l'interface `IMarkContained`::
>>> from zope.interface.verify import verifyClass
>>> verifyClass(IMarkContained, Mark)
True
Une exemple d'utilisation de l'url de Mark::
>>> mk = Mark()
>>> mk.url
u'http://www.zope.org'
>>> mk.url = u'http://www.python.org'
>>> mk.url
u'http://www.python.org'
Une exemple d'utilisation de la description de Mark::
>>> mk = Mark()
>>> mk.description
u''
>>> mk.description = u'Zope Project Web Site'
>>> mk.description
u'Zope Project Web Site'
"""
implements(IMark, IMarkContained)
url = u"http://www.zope.org"
description = u""
class BookMarker(BTreeContainer):
"""Implémentation de IBookMarker utilisant B-Tree Container
Assurons nous que `BookMarker` implémente l'interface `IBookMarker`::
>>> from zope.interface.verify import verifyClass
>>> verifyClass(IBookMarker, BookMarker)
True
Un exemple de changement du nom du BookMarker::
>>> bm = BookMarker()
>>> bm.name
u''
>>> bm.name = u'MonBookMarker'
>>> bm.name
u'MonBookMarker'
"""
implements(IBookMarker)
name = u""
Nous avons écrit des doctests avec l'implémentation. Les doctests sont accompagné d'exemples, c'est ce que l'on appelle des tests unitaire conduit par des exemples.
Configuration
Maintenant la configuration dans le fichier configure.zcml (boom/configure.zcml) (zcml est le un format XML de configuration des composants Zope):
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="bookmarker">
<interface
interface=".interfaces.IBookMarker"
type="zope.app.content.interfaces.IContentType"
/>
<class class=".bookmarker.BookMarker">
<implements
interface="zope.annotation.interfaces.IAttributeAnnotatable"
/>
<implements
interface="zope.app.container.interfaces.IContentContainer"
/>
<factory
id="boom.bookmarker.BookMarker"
description="Gestionnaire de marque pages"
/>
<require
permission="zope.ManageContent"
interface=".interfaces.IBookMarker"
/>
<require
permission="zope.ManageContent"
set_schema=".interfaces.IBookMarker"
/>
</class>
<interface
interface=".interfaces.IMark"
type="zope.app.content.interfaces.IContentType"
/>
<class class=".bookmarker.Mark">
<implements
interface="zope.annotation.interfaces.IAttributeAnnotatable"
/>
<factory
id="boom.bookmarker.Mark"
description="un marque page."
/>
<require
permission="zope.ManageContent"
interface=".interfaces.IMark"/>
<require
permission="zope.ManageContent"
set_schema=".interfaces.IMark"
/>
</class>
<browser:addform
label="Ajouter un gestionnaire de marque page"
name="AddBookMarker.html"
schema="boom.interfaces.IBookMarker"
content_factory="boom.bookmarker.BookMarker"
fields="name"
permission="zope.ManageContent"
/>
<browser:addMenuItem
class=".bookmarker.BookMarker"
title="Gestionnaire de marque pages"
permission="zope.ManageContent"
view="AddBookMarker.html"
/>
<browser:editform
schema="boom.interfaces.IBookMarker"
for="boom.interfaces.IBookMarker"
label="Modifier le gestionnaire de marque page"
name="edit.html"
permission="zope.ManageContent"
menu="zmi_views" title="Edit"
/>
<browser:containerViews
for="boom.interfaces.IBookMarker"
index="zope.View"
contents="zope.View"
add="zope.ManageContent"
/>
<browser:addform
label="Ajouter un marque page"
name="AddMark.html"
schema="boom.interfaces.IMark"
content_factory="boom.bookmarker.Mark"
fields="url description"
permission="zope.ManageContent"
/>
<browser:addMenuItem
class="boom.bookmarker.Mark"
title="Marque page"
description="URL of Website"
permission="zope.ManageContent"
view="AddMark.html"
/>
<browser:editform
schema="boom.interfaces.IMark"
for="boom.interfaces.IMark"
label="Modifier un marque page"
fields="url description"
name="edit.html"
permission="zope.ManageContent"
menu="zmi_views" title="Edit"
/>
<!--
<browser:page
name="marks.html"
for="boom.interfaces.IBookMarker"
class=".browser.BookMarks"
template="marks.pt"
permission="zope.Public"
menu="zmi_views"
title="Marque pages"
/>
-->
</configure>
Tout ça parrait limpide ? "non... !", ok! nous allons discuter du langage de configuration de Zope (ZCML, Zope Configuration Markup Language) un peu plus tard. Maintenant, si vous êtes familier avec le ZCML, cette configuration est bien plus que limpide. Cela devrait vous donner un aperçu de l'application entière. Vous pourriez penser que ce nc'est pas très Pythonic :( Hey, reflechissez y à deux fois !
Lancez le !
Comme dernière étape pour construire l'application, ajouter la ligne suivante dans un fichier $HOME/myzope/etc/package-includes/boom-configure.xml :
<include package="boom" />
Maintenant vous avez enregistré votre paquage pour Zope.
Lancez Zope a nouveau, ouvrez votre navigateur dans l'interface d'administration http://localhost:8080/manage, ajouter un gestionnaire de marque pages et quelques marque pages.
Maintenant vous aimeriez obtenir vos marques pages d'une meilleur façon non ? Pour l'instant, relaxez, après nous allons créer une présentation pour nos marques pages.
Allons-y
Vue (vue de Model View Controleur)
Maintenant creez un fichier nommé browser.py avec le code suivant (boom/browser.py):
from boom.interfaces import IMark
class BookMarks:
def __init__(self, context, request, base_url=''):
self.context = context
self.request = request
self.base_url = base_url
def listMarks(self):
marks = []
for name, child in self.context.items():
if IMark.providedBy(child):
info = {}
info['url'] = child.url
info['description'] = child.description
marks.append(info)
return marks
Et pour la présentation un template dans le fichier marks.pt (boom/marks.pt):
<html metal:use-macro="views/standard_macros/view">
<body>
<div metal:fill-slot="body">
<div class="row">
<div class="label">Book Marks:</div>
<br/><br/>
<li tal:repeat="item view/listMarks">
<a href="" tal:attributes="href item/url">
<span tal:content="item/url">Link</span>
</a>
<pre tal:content="item/description">Description</pre>
<br/>
</li>
</div>
</div>
</body>
</html>
Notez que ce template de page web utilise du XML valide et qu'il reste ainsi éditable par la plupars des éditeurs de page web.
Vous pouvez décommenter la dernière balise xml 'browser:page' de votre fichier de configuration (boom/configure.zcml). Si vous relancez Zope vous verrez maintenant un onglet "Marque pages" affichant tout vos marque pages.
Ok! ce n'est pas la fin, ce n'est que le début de votre apprentissage.
Tests fonctionnel
Finissons notre exemple par l'écriture de tests fonctionnel pour notre vue. Notre code sera placé dans le module ftests.py (boom/ftests.py):
import unittest
from zope.app.testing.functional import BrowserTestCase
class BookMarksTest(BrowserTestCase):
def testAddBookMarker(self):
response = self.publish(
'/+/AddBookMarker.html=bm',
basic='mgr:mgrpw',
form={'field.name': u'MyBookMarker',
'UPDATE_SUBMIT': 'Add'})
self.assertEqual(response.getStatus(), 302)
self.assertEqual(response.getHeader('Location'),
'http://localhost/@@contents.html')
def testAddMark(self):
self.testAddBookMarker()
response = self.publish(
'/bm/+/AddMark.html=mrk',
basic='mgr:mgrpw',
form={'field.url': u'http://www.zope.org',
'field.description': u'Zope Project Site',
'UPDATE_SUBMIT': 'Add'})
self.assertEqual(response.getStatus(), 302)
self.assertEqual(response.getHeader('Location'),
'http://localhost/bm/@@contents.html')
def testMarksListing(self):
self.testAddMark()
response = self.publish(
'/bm/@@marks.html',
basic='mgr:mgrpw')
body = response.getBody()
self.checkForBrokenLinks(body, '/bm/@@marks.html',
basic='mgr:mgrpw')
self.assert_(body.find('Zope Project Site') > 0)
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(BookMarksTest),
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
Pour lancer les tests fonctionnels :
$ cd ~/myzope/
$ ./bin/test -vpf --dir boom
Maintenant quoi ?
Maintenant vous pouvez commencez à apprendre Zope 3 en détail, par exemple en utilisant le livre Zope 3 (en anglais). Vous pouvez aussi rejoindre la mailing liste zope3-users.
Il y a un bon guide de démarage sur Zope 3 par Benji York : http://www.benjiyork.com/quick_start/
Un bon livre d'introduction est aussi disponible en librairies, visitez : http://worldcookery.com/
Encore une petite chose : Je veux améliorer ce document, n'hésitez pas à écire vos commentaire à Ikipou AT (@) gmail.com.
La grande image
Hey, il est quel heure maintenant ?
Oh!, celà fait seulement 10 minutes depuis que nous avons commencé à lire cette article. Il nous reste 20 minutes pour expliquer ce qu'on a fait ici, retournons-y.
Installation et configuration
Vous pouvez télecharger Zope 3.3.0 ou superieur pour vos traveaux majeurs. Il est commun d'installer Zope 3 en root bien que ce ne soit pas obligatoire. Quand vous experimentez avec Zope 3, créez une instance pour un utilisateur sans privilège. Pour les systèmes de production, vous gagnez ainsi l'avantage de la sécurité au niveau de l'OS.
Puisque nous avons installé manuellement Zope 3 (en root), le repertoire d'installation par défaut est /usr/local/Zope-3.x.x. Lorsque vous créez une instance Zope, vous devez spécifier un login et mot de passe pour le manager de ZMI. Vous pouvez faire plus d'instance, celon les besoins, la politique de Zope est d'utiliser une instance par application web.
Dans votre dossier d'instance zope (ici myzope), il y a plusieurs repertoire pour des besoins spécifique. Ici, j'utiliserais $ZOPE3INSTANCE pour me réferer à votre instance.
$ZOPE3INSTANCE/etc
Dans 'etc' vous pouvez retrouver les fichiers de configuration. Dans etc/zope.conf vous pouvez changer le port des serveurs http et ftp.
Zope sauve toutes les données ici par défaut (dans une base de donnée objet, la ZODB). C'est le dossier à backuper pour un système de production. Si le fichier 'Data.fs' est manquant, un fichier vierge sera créé automatiqment au prochaine lancement de Zope.
$ZOPE3INSTANCE/binQuelques scripts utilitaire. 'runzope' et 'test' vous sont déjà familier. Le script 'pyskel' est très utile, il crée le squelette du code a partir des définitions des interfaces, par exemple :
$ $ZOPE3INSTANCE/bin/pyskel boom.interfaces.IMark > file.py
Le script 'debugzope' est aussi très utile, il permet d'obtenir un prompt Python avec les chemins de recherche de l'instance configuré. Il permet aussi d'utiliser directement les outils Zope et la base de donnée objet (ZODB).
$ZOPE3INSTANCE/lib/python
Comme je l'ai mentionné plus tôt, vous pouvez mettre vos packages Zope 3 ici. Par défaut, ce dossier en sera pas dans votre chemin de recherche Python en dehors de Zope. Vous pouvez utiliser le script 'debugzope' pour obtenir un prompt utilisant ce dossier.
Pourquoi devrais-je coder comme ça ?
Lorsque nous avons commencé l'application marque pages, le premier point sur lequel j'ai insisté est l'Extreme Programming (XP). Je vous recommande fortement de lire sur l'Extreme Programming. Cela aura vraiment une influence pour écrire du code de la même façon que Zope (directement et indirectement). Ok, un avertissement : les XPers (experts de l'XP) pensent que les framework sont néfaste. Ici je ne peux pas donner de réponse en une phrase et vous devrez trouver la réponse par vous même. "Extreme Programming Explained" par Kent Beck sera un bon point de départ pour votre apprentissage de l'XP.
Un autre livre classique que vous pouvez lire est "Design Patterns" par la bande des quatres (le GoF, Gang of Four). Dans ce livre ils disent "programmez pour une interface, par pour une implémentation". Pour simuler une définition formelle des interfaces en C++, ils utilisent des classes avec des fonctions virtuelles. De façon similaire, nous utilisons des classes héritant de la meta-classe zope.interface.Interface pour définir une interface. Il y a de grande chance qu'il y aura un concept explicite d'interface dans la version 3 de Python.
Selon l'Extreme Programming : "Les quatres activités du developpement sont : codage, tests, être à l'écoute et designer. Zope 3 rend le test logiciel intégré et facile, vous pouvez écrire des tests unitaire et fonctionnel très facilement.
Regardons les interfaces de plus près
J'espère que vous êtes déjà familier avec le concept d'interface. Quand nous parlons d'une interface, nous l'utilisons en référence avec un objet (une instance de classe). Par exemple si 'A' est une classe qui implémente 'IA', et que 'a' est une instance de 'A', alors 'IA' est l'interface de 'a', ça marche ? Souvent on ne travaillera qu'avec les interfaces et les classes qui l'implémentent.
Dans notre fichier interfaces.py nous avons définit trois interfaces. La première interface 'IMark' définit un marque page. Un marque page à deux attributs, le premier est l'url, le second la description. Nous avons utilisés les champs définit dans zope.schema pour spécifier ces attributs. Un schema est une interface avec les attributs définit à l'aide de champs de zope.schema. Donc, nos IBookMarker, IMark et IMarkContained sont des schéma, est non pas de simples interfaces.
Comme mentionné plus tôt, IBookMarker est une interface container. Nous avons étendu IContainer en ajoutant l'attribut name. Nous avons aussi ajouté une précondition sur le type du contenu. Les conditions sur les attributs et les méthodes sont appliqué par Zope au niveau de la définition, si bien que toutes les classes implémentant l'interface ces restrictions sans code suplémentaire (pour la validation de formulaire par exemple). Les objects d'IBookMarker ne peuvent contenir que des objects de type IMark. Avant d'avancer vers la section suivante, notez l'origine des modules et paquages des classes et interfaces que nous avons utilisé. Ouvrez les sources de ces modules et voyez a quel point tout est bien documenté.
De la documentation suplémentaire est disponible dans des fichiers séparé avec les tests unitaires, c'est le moyen de Zope 3 pour mettre en oeuvre plus de tests unitaire.
Les tests unitaire revisités
Nous avons écrit nos tests unitaire dans un fichier nommé tests.py. vous pouvez aussi écrire les tests dans un paquages nommé tests. Tout les modules de tests dans ce paquage doivent avoir le prefixe test_. Les modules utilitaires de ces tests ne devrait pas commencer par ce prefix. Avec ces conventions, le scripts de tests automatisé de Zope 3 devrait détecter tout vos modules de test.
Pour tester notre container BookMarker, nous héritons de TestSampleContainer. Voyez que même les modules de tests sont réutilisables avec Zope! Beaucoup de tests que nous aurions à écrire pour un container se trouvent dans TestSampleContainer.
La classe DocTestSuite nous aide à intégrer tout les doctests avec le module unittest. Ceci nous permet de lancer tout les tests automatiquement. Parfois vous devrez écrire plus de code pour les tests que pour l'implémentation elle même. Graduellement, cela accellerera votre développement, si ce n'est pas le cas c'est qu'il y a un problème dans votre processus de developpement.
Parlons des implémentations
La première ligne de bookmarker.py fixe la variable spéciale __docformat__ = 'restructuredtext'. Notre documentation est écrite à l'aide de ce format ReStructuredText. J'encourage fortement l'utilisation de ReST dans toutes vos documentations Python (L'article original est écrit en ReST : svn co svn://svn.berlios.de/zissue/trunk/z3in30m).
Nous commençons par importer la fonctions implements() du package zope.interface. Utilisant cette fonction nous pouvons dire quel interfaces sont implémentés par une classe. BTreeContainer est une implémentation complète de l'interface IContainer. Nous héritons de celle-ci pour implémenter notre interface Container.
De la même façon, Contained est une implémentation complète de IContained (hériter de IContained n'est pas obligatoire pour utiliser un objet dans un container mais celà nous permet ici d'utiliser la ZODB pour la persistance et d'utiliser les contraintes). La classe Mark implémente les deux interfaces IMark et IMarkContained. IMarkContained est une interface étendant IContained. Alors en héritant de la classe Contained nous obtenons une implémentation partielle de nos interfaces. Il nous reste à implémenter les deux attributs, URL et description.
J'espère que la documentation des classes est suffisament explicite.
ZCML expliqué
Laissez moi poser une question, que vous aller retrouver souvent : pensez vous que un langage de programmation devrait être utilisé pour écrire des fichiers de configuration? Si oui, pourquoi? Pourquoi ne pas utiliser un fichier texte avec des convention de format.
ZCML est le format XML de configuration de Zope 3. Le drapeau de base 'configure' spécifie les espaces de noms à utiliser. Dans notre fichier de configuration nous avons utilisé deux espaces de noms : 'zope' et 'browser'. L'espace de nom 'zope' contient les éléments de base pour enregistrer nos type de données. L'espace 'browser' permet de configurer tout ce qui concerne la présentation dans un navigateur web (zope gère aussi des vue en FTP, XML-RPC, WebDAV).
Le premier drapeau utilisé est 'interface', un drapeau ZCML est appellé une directive. Dans notre configuration 'interface' est une directive simple, 'class' est une directive complexe.
Les vue et ZPT
Vos objets sont finallement sauvé dans la ZODB, vous pouvez aussi choisir d'autres mechanismes de stockage (principalement SQL et des fichiers sur le système). Dans Zope vous pouvez présenter vos objets de diverses façon. Cela implique différent protocol comme http, ftp, xmlrpc etc. mais aussi de diverse façon par protocole (plusieurs présentation d'un même contenu en page web). Dans notre cas nous avons créé une vue pour les navigateurs.
Nous avons mis la logique de présentation dans un module browser.py. Nous avons créé un temple Zope pour la présentation (ZPT, Zope Page Template). A l'aide de ZPT vous pouvez écrire du code de présentation sans la moindre logique applicative. Dans ZCML vous utilisez la directive browser:page pour enregistrer une vue.
Finissons
Les choses que nous n'avons pas mentionné
Allez vite à la prochaine section sinon vous ne pourrez jamais finir l'article dans les 30 minutes :)
Bien sur dans un document d'introduction il n'est possible que de couvrir qu'une fraction de toute la technologie. Ici beaucoup de choses ont été omises pour diverses raisons.
Si vous êtes interessé par Zope 3 maintenant, vous devriez certainement explorer beaucoup plus le framework. Pendant votre exploration vous découvrirez que les doctests peuvent être écrit dans des fichier séparé. De la même façon vous comprendrez beaucoup de chose, et vous verrez le formidable découplage réalisé entre tout les composants de Zope.
La fin
Oh! c'est juste la fin de l'article et le merveilleux commencement de votre voyage dans le monde de Zope 3. Alors bonne chance.
A propos de l'auteur
L'auteur du text original est un developpeur Python de Kerala en inde. Il travail beaucoup avec Python, principalement sur PyGTK et PostgreSQL. C'est un utilisateur de GNU Emacs.

