You are not logged in Log in Join
You are here: Home » Members » nexedi » Education Material » Comment cr�er un produit avec Zope

Log in
Name

Password

 

Comment cr�er un produit avec Zope

Lastest versions are published here

La cr�ation d'un produit sous Zope.

Introduction.

Puisque je travaille sous Zope, je me suis demand� comment il fallait proc�der pour cr�er un produit. J'ai donc commenc� � chercher de la documentation. J'ai trouv� plusieurs sources: le livre Zope par Pierre-Julien Grizel, en fran�ais, quelques tutoriaux en anglais sur le site de Zope, ainsi que le Zope Developer Guide, �galement en anglais. Apr�s quelques tribulations, puisque ces sources, sauf la derni�re, se sont r�v�l�es ne pas pouvoir me permettre de cr�er un produit pour Zope, je vais donc relater mon exp�rience dans ce domaine. Cet �crit est, donc, en grande partie inspir� du Zope Developer Guide.

Les produits permettent d'�tendre les fonctionnalit�s de Zope. Ils s'agit souvent de pouvoir cr�er de nouveaux types d'objets, mais aussi, parfois, de nouvelles balises (tags) DTML, de nouvelles classes construites sur la classe ZClasse, etc... Bien qu'il y ait 2 mani�res de cr�er de nouveaux produits Zope: par l'interm�diaire du web ou par l'ajout de fichiers dans la hi�rarchie de fichiers, je ne m'int�resse ici qu'� cette derni�re m�thode. Si vous voulez de plus amples explications sur la premi�re m�thode, allez consulter le Zope Book chapitre 12.

Processus de d�veloppement.

Je consid�re, dans la suite de ce texte, que la r�alisation d'un produit r�pond exactement � vos besoins. Cependant, le Zope Developer Guide faisant de m�me, je dois vous mettre en garde de bien consid�rer toutes les options avant de commencer: des ZClasses, des m�thodes externes, des scripts pythons ne conviendraient-ils pas mieux? Sachez que les produits sont parfaits pour ajouter � Zope de nouvelles classes. Si ceci n'est pas un point cl� de votre solution, cherchez ailleurs. Sachez �galement que les produits, mais aussi les m�thodes externes, permettent l'�criture de code python non limit�. Vous �tes donc maintenant pr�venu, ne vous plaignez pas plus tard! :)

L'interface de votre produit.

La premi�re chose � faire pour cr�er un produit est de fixer l'interface qu'il pr�sentera � l'ext�rieur. La cr�ation d'interfaces est parfaitement expliqu�e dans le Zope Developer Guide, vous pouvez donc, pour plus d'informations, vous y r�f�rer. Le produit que je vais cr�er ne n�cessiterais pas normalement d'interface, mais c'est une bonne habitude � prendre, pour plusieurs raisons: d'abord, Zope est lui-m�me en train d'�tre r��crit de cette fa�on, par ailleurs, tout utilisateur de votre produit trouvera plus facilement comment l'utiliser s'il n'a acc�s qu'aux informations dont il a besoin, c'est-�-dire sur les seules fonctions qu'il a le droit d'utiliser, c'est le fameux principe de r�tention d'information de la programmation OO:

      from Interface import Base

      class Nexedi_Minimal(Base):
        """Un produit qui ne fait rien sinon afficher une valeur entiere"""

        def set_value(self, valeur):
          """fixons la valeur du produit"""

        def get_value(self):
          """affichons donc cette valeur"""

Dans notre cas particulier, les deux fonctions propos�es ne sont l� que pour justifier la cr�ation d'une interface � notre classe, parce que comme vous pouvez d�j� le deviner, les corps de ces fonctions seront on ne peut plus simple! Remarquez que le choix du nom de l'interface est laiss� � votre appr�ciation.

L'impl�mentation de votre produit.

Vous avez donc maintenant une interface pour votre produit. C'est bien! Mais ce qui est mieux c'est de pouvoir faire ce que l'interface annonce. Nous allons donc maintenant impl�menter notre interface:

      from Nexedi_Minimal import Nexedi_Minimal

      class Nexedi_Product:
        """"La veritable implementation de notre produit"""

        __implements__ = Nexedi_Minimal

        def __init__(self, id, valeur="0"):
          """initialisation"""
          self.id = id
          self.valeur = valeur

        def set_value(self, valeur="0"):
          """on attribue une valeur a notre objet"""
          self.valeur = valeur

        def get_value(self):
          """on obtient la valeur de notre objet"""
          return self.valeur

Donc maintenant on a une v�ritable impl�mentation, m�me si notre produit ne fera pas grand chose d'exceptionnel! Notez bien qu'il est donc maintenant possible de tester notre code sous Python et ceci de mani�re interactive. Cette possibilit� peut �tre d'une grande aide en cas de tests ou de d�boguage. Bon mais pour l'instant, tout ce qu'on a c'est une classe avec documentation et interface, pas encore un produit fini.

Cr�ation de la classe de notre produit.

Nous avons donc un classe qui sera la base de notre produit, mais pour v�ritablement la transformer, il reste du chemin. Tout d'abord, il faut savoir qu'un produit doit respecter un certain nombre de r�gles. Pour �tre un produit valide, il faudra d'abord que notre classe d�rive de nombreuses classes de base. Pour cela, il faut rajouter ceci au d�but de notre fichier:

        from Acquisition import Implicit
        from Globals import Persistent
        from AccessControl.Role import RoleManager
        from OFS.SimpleItem import Item

        class Nexedi_Produit(Implicit, Persistent, RoleManager, Item):
          """..."""

C'est bien joli tout �a, mais � quoi �a sert? Je vais donc d�crire l'utilit� de ces classes en commen�ant par la derni�re d'entre elles: OFS.SimpleItem.Item.C'est cette classe qui permet de travailler avec Zope, elle fournit un certain nombre de propri�t�s � notre classe, entre autres la possibilit� du copier/coller, la possibilit� d'utiliser l'interface web de management. C'est vraiment une classe indispensable pour les fonctionnalit�s de base. Pour plus de renseignements sur cette classe, vous devriez aller voir son code, je vous le conseille vivement. Pour que tout fonctionne, il vous faut ajouter quelques attributs � votre classe de base: un attribut meta_type et un attribut id. Notre nouveau code ressemble donc � ceci:

      class Nexedi_Produit(Implicit, Persistent, RoleManager, Item):
        """"La veritable implementation de notre produit"""

        __implements__ = Nexedi_Minimal

        #cette ligne a ete rajoutee
        meta_type = 'Nexedi_Minimal'

        def __init__(self, id, valeur="0"):
          """initialisation"""
          #cette ligne etait deja la mais est necessaire pour la classe Item
          self.id = id
          self.valeur = valeur

Il est � noter que le choix de la valeur de l'attribut meta_type est important: c'est le nom qui appara�tra dans Zope, dans la liste des objets qui peuvent �tre instanci�s. Par ailleur, Zope demande que tout objet cr�� est une id propre. Vous voil� maintenant au courant de l'importance de ces deux modifications.

Continuons dans nos explications: la classe AccessControl.Role.RoleManager. H�riter de cette classe permet de pouvoir g�rer au mieux la s�curit� du produit qui va �tre cr�� et donc, c'est un h�ritage indispensable. Je ne vais pr�ciser � personne combien la s�curit� est importante pour un site web.

La classe Persistent est la classe qui va permettre � vos objets de se voir sauvegarder sous Zope. Je n'ai jamais r�ussi � instancier un objet de mon produit sans cet h�ritage: Zope ne voulait tout simplement pas entendre parler d'un objet qu'il n'aurait pas pu sauvegarder!

Finalement la classe Acquisition.Implicit: il est possible d'h�riter de la classe Acquisition.Explicit, mais bon, de mon point de vue, dans la r�alisation de mon produit, je n'ai pas vu de grande diff�rence. Cependant, il peut �tre bon de consulter le Zope Book ou bien le Zope Developer Guide qui contiennent plus de pr�cisions sur l'acquisition. Dans mon cas, sans cet h�ritage, j'ai rencontr� �norm�ment de probl�me par rapport � l'objet REQUEST lors du travail avec des instances de mon produit. Je rappelle bri�vement, qu'en gros, l'objet REQUEST est l'objet qui repr�sente l'URL demand�e par le client. Donc, sans cet h�ritage, il m'�tait impossible d'obtenir les onglets de management pour des instances de mon produit. Il m'�tait donc impossible d'obtenir plus d'un acc�s � mon objet: je pouvais voir le r�sultat mais pas la page modification, ni aucune autre, et vice et versa... :( Et super le produit ing�rable!

La s�curit� dans le produit.

Sans les d�finitions ad�quates des permissions sur chaque m�thode du produit, il est tout simplement impossible de cr�er des instances de ce produit sous Zope. En plus de faire ces d�clarations, Zope demande � ce que la classe du produit soit initialis�e. Donc notre code ressemble maintenant � �a:

        #ces deux premieres lignes ont ete ajoutees
        from AccessControl import ClassSecurityInfo, getSecurityManager
        from Globals import InitializeClass
        from Acquisition import Implicit
        from Globals import Persistent
        from AccessControl.Role import RoleManager
        from OFS.SimpleItem import Item

        class Nexedi_Produit(Implicit, Persistent, RoleManager, Item):
          """produit pour apprendre."""

          __implements__ = Nexedi_Minimal

          meta_type = 'Nexedi_Minimal'

          def __init__(self, id, valeur="0"):
            """initialisation"""
            #cette ligne etait deja la mais est necessaire pour la classe Item
            self.id = id
            self.valeur = valeur

          #cette ligne permet de creer un objet pour plus tard definir les permissions autorisees
          security=ClassSecurityInfo()

          def __init__(self, id, valeur="0"):
            """Constructeur, methode d'initialisation"""
            self.id = id
            self.valeur = valeur

          security.declarePublic('set_value')
          def set_value(self, valeur="0"):
            """on attribue une valeur a notre objet"""
            self.valeur = valeur

          security.declarePublic('get_value')
          def get_value(self):
            """on obtient la valeur de notre objet"""
            return self.valeur

          # Initialize product class
          # ------------------------

          InitializeClass(Nexedi_Produit)

Finaliser le produit.

Nous avons donc ici une classe dont les permissions ont �t� parfaitement d�finies. Et donc maintenant le produit est presque termin�, il ne reste plus qu'� l'enregistrer sous Zope pour qu'il soit utilisable. Pour ce faire, nous allons lui rajouter un certain nombre de propri�t�s, et en particulier, un formulaire qui servira � la cr�ation d'instances de ce produit, et �galement un fichier qui d�clarera � Zope ce qui peut �tre utilis�, le fichier __init__.py dans le r�pertoire de notre fichier. Voyons comment ceci se construit:

  • Nous allons d'abord ajouter � notre produit de quoi initialiser les attributs de la nouvelle instance cr��e, son id et sa valeur. Pour cela nous allons lui ajouter ce qui est appel�e des fabriques (en anglais factories). Tr�s exactement, il y aura un formulaire qui sera appel� lors de la cr�ation. Ce formulaire lui-m�me appelera une fonction qui sera donc charg�e de l'initialisation de notre produit, avec appel au constructeur de l'objet. Penchons-nous d'abord sur le formulaire. Il sera �crit en DTML et pour une meilleure hi�rarchisation des fichiers du produit, nous allons cr�er dans son arborescence un sous-r�pertoire appel� dtml, dans lequel nous l'y placerons ainsi que d'autres �ventuels fichiers dtml. Ce formulaire prend cette forme:
                 <form action="addFunction"><br>
                   <table cellspacing="2">
                     <tr>
                       <th class="form-label">id</th>
                       <td><input type="text" name="id:string" size="25" class="form-element"></td>
                     </tr><tr>
                       <th class="form-label">valeur</th>
                       <td><input type="text" name="valeur:string" size="25" class="form-element"></td>
                     </tr><tr>
                       <th></th>
                       <td><input class="form-element" type="submit" name="Create"></td>
                     </tr>
                   </table>
                 </form>
    

Nous notons que ce fichier est principalement constitu� d'un formulaire sans nom, contenant un tableau dont la fonction est d'organiser la r�cup�ration des diff�rentes valeurs de l'instance du produit nouvellement cr��e. Ce formulaire fera appel, quand il lui sera notifi� de le faire par action sur le bouton nomm� Create, � la fonction addFunction qui, comme expliqu� pr�c�demment, a pour fonction de cr�er l'instance du produit. Ce fichier est �crit en un m�lange classique de HTML et de DTML. Si vous d�sirez plus de renseignement sur le DTML, je vous conseille d'aller consulter soit le Zope book, soit le Zope Developer Guide. Cependant, vous pouvez d�j� voir que la fonction addFunction aura au moins deux param�tres de type string et nomm�s id et valeur. Ce sont bien ceux que nous avions choisi pr�c�demment d'utiliser. Le formulaire est maintenant pr�t, mais il ne s'appelle pas tout seul, il nous faut le signaler dans la classe du produit que nous cr�ons:

       #cette ligne n'est pas rajoutee dans la classe mais juste avant
       # et permet la declaration du formulaire precedent
       #a supposer qu'il est ete sauvegarde dans le fichier edit.dtml
       # dans le sous-repertoire dtml.
       addForm=DTMLFile('dtml/edit', globals())

Ensuite, il nous faut �crire la fonction addFunction qui est appel�e dans ce formulaire. Elle a donc deux param�tres, id et valeur et permet la cr�ation de l'instance de notre produit. Elle se trouve juste apr�s la ligne pr�c�dente, juste avant la classe Nexedi_Produit:

       def addFunction(dispatcher, id, valeur="0", RESPONSE=None, REQUEST={}):
         """addFunction() -> nothing
         Insere l'objet dans son conteneur et retourne la page index_html"""
         dispatcher.Destination()._setObject(id, Nexedi_Produit(id, valeur))
         #  REQUEST.RESPONSE.redirect('index_html')

Cette fonction re�oit en param�tres cinq �l�ments: RESPONSE, REQUEST qui constituent l'environnement de notre cr�ation d'instance, id, valeur qui est � "0" par d�faut, ce sont les valeurs des attributs de l'objet nouvellement cr��. Il est important pour le choix de l'id de notre instance de respecter la r�gle d'unicit� classique sous Zope. Il y a finalement, ou plus exactement ici au d�but un param�tre nomm� dispatcher: cet objet procure l'endroit o� notre instance est cr��e. C'est d'habitude un r�pertoire. Il appelle la m�thode _setObject() sur son attribut Destination: Destination est un objet de type ObjectManager et repr�sente donc l'emplacement de cr�ation de notre objet. La fonction appel�e permet la cr�ation effective de l'objet. Vous pouvez obtenir plus d'information sur la classe ObjectManager en consultant le Zope Book ou le Zope Developer Guide. Notez ici que la derni�re ligne est un commentaire. En fait cette ligne permet, apr�s instanciation, de sortir et de retourner une page d'accueil. Ici, en l'occurence cette page est la page index_html du r�pertoire dans lequel vient d'�tre cr��e notre instance.

  • Le produit que nous venons de construire doit en fait devenir un package plac� dans le bon r�pertoire pour que Zope puisse le trouver et le rendre accessible � son d�marrage. C'est pourquoi, comme tout package python, il doit poss�der un fichier __init.py__ dans son r�pertoire. Ce fichier se construit de la sorte:
              from Nexedi_Produit import Nexedi_Produit, addForm, addFunction
    
              def initialize(registrar):
                registrar.registerClass(
                  Nexedi_Produit,
                  constructors = (addForm, addFunction),
                  )
    

Comme vous l'avez remarqu�, on a donc rendu accessible, � l'ext�rieur, la classe produit elle-m�me ainsi que la fonction et le formulaire qui permettent � la cr�ation de notre produit de d�finir tous les �l�ments dont il a besoin, en particulier son id.

Arriv� � ce point, nous en sommes � avoir un produit quasi-fini. Il ne manque plus, pour rendre le produit plus utilisable, que les pages de management du produit, management qui peut se faire via le web, avec, entre autres, les pages de modification de la valeur de l'objet et de visualisation de cette valeur. Le produit pr�sentera donc, selon le r�le endoss� par l'utilisateur, plusieurs pages s�lectionnables par l'interm�diaire d'une s�rie d'onglets (en anglais tabs). Il faut d'abord d�finir quels onglets seront pr�sents. Dans le code de la classe-produit, il nous faut rajouter:

            from Globals import DTMLFile

            (...)

            Class Nexedi_Produit(...):

            manage_options=(
                {'label' : 'Edit', 'action' : 'editForm'},
                {'label' : 'View', 'action' : 'index_html'},
                ) + RoleManager.manage_options + Item.manage_options

            security.declarePublic('index_html')
            index_html=DTMLFile('dtml/view', globals())

            security.declareProtected('View management screens','editForm')
            editForm=DTMLFile('dtml/change', globals())

            security.declareProtected('Change Nexedi_Minimal', 'editFunction')
            def editFunction(self, valeur, RESPONSE=None):
              """la fonction pour modifier la valeur"""
              self.valeur = valeur
              RESPONSE.redirect('index_html')

Nous avons donc indiqu� quels seront les onglets pr�sents: un onglet Edit, qui permettra donc de changer la valeur de notre objet, un onglet View qui permet de voir notre objet publi� et enfin les onglets h�rit�s de RoleManager, l'onglet Security qui permet de g�rer les permissions accord�es sur notre objet, et de Item, les onglets Undo ( D�faire ) et Ownership ( Propri�t� ). Nous avons ici utilis� la forme minimale de description des onglets: � savoir que nous n'avons d�fini que les valeurs, obligatoires!!, label et action dont les sens sont �vidents, il est �galement possible d'y ajouter target, qui permet de sp�cifier le cadre dans lequel le r�sultat de l'action est affich�, et help au contenu �vident. Par ailleurs, nous avons ajout�, en utilisant la fonction DTMLFile qui transforme une cha�ne de caract�res en un fichier dtml, les liens vers les fichiers de modifications et d'�dition de notre objet. Ils ont bien �videmment re�us leur niveau de visibilit� � l'ext�rieur de la classe. Il me reste � donner les fichiers view.dtml et change.dtml qui seront plac�s dans le r�pertoire dtml de notre produit. Ils seront vraiment basiques. Pour view.dtml:

               <table cellspacing="2">
                 <tr>
                   <th>pour notre objet dont l'id est : </th>
                   <td><dtml-var id></td>
                 </tr><tr>
                   <th>la valeur contenue est : </th>
                   <td><dtml-var valeur></td>
                 </tr>
               </table>

et pour change.dtml:

             <dtml-var manage_tabs>
             <form action="editFunction"><br>
               <table cellspacing="2">
                 <tr>
                   <th>pour notre objet dont l'id est : </th>
                   <td><dtml-var id></td>
                 </tr><tr>
                   <th>ancienne valeur : </th>
                   <td><dtml-var valeur></td>
                 </tr><tr>
                   <th class="form-label">nouvelle valeur : </th>
                   <td><input type="text" name="valeur:string" size="25" class="form-element"></td>
                 </tr><tr>
                   <th></th>
                   <td><input class="form-element" type="submit" name="Change"></td>
                 </tr>
               </table>
             </form>

Transformer le produit en un package utilisable.

Le produit est termin�. Il ne reste plus qu'a lui ajouter un icone et � le transformer en package. L'icone doit �tre un fichier .gif qui se place en g�n�ral dans un sous r�pertoire nomm� www. Ce doit �tre une image de 16 par 16. Par ailleurs, il faut rendre cet icone accessible en rajoutant une ligne dans le fichier __init__.py:

            registrar.registerClass(
                (...)
                icon = 'www/Nexedi_Produit.gif'
                )

Maintenant vous pouvez y rajouter un fichier README.txt, un fichier LICENCE.txt, et d'autres... Pour plus de pr�cision sur ces fichiers, allez consulter le Zope Developer Guide sur zope.org . Et finalement il ne reste plus qu'� le tar-zipper le produit. Apr�s vous �tre rendu dans le r�pertoire de Zope, tapez quelque chose comme �a:

            $ tar cvfz Nexedi_Product-1.0.0.tgz lib/python/Products/Nexedi_Product

Et voil�!! Vous pouvez livrer votre produit!!

Astuce finale:

Il se peut qu'au cours de la cr�ation du produit, des bogues apparaissent (Si! Si! Je vous assure! :). Stopper et red�marrer Zope � chaque modification du produit peut alors rapidement devenir tr�s lassant. Il existe donc ici une technique � mettre en oeuvre pour �viter ces longueurs: Zope permet de rendre un produit auto-refreshable, c'est-�-dire qui se met � jour automatiquement, et en plus sans arr�t de Zope. Je vous conseille donc de mettre en place cette option. Le plus simple est d'aller sous Zope.org , et l� de rechercher refresh puis de suivre les instructions. Je vous assure, pour l'avoir essay�e, que cette option est un pur bonheur quand il s'agit de faire de petites modifications au produit en cours d'�criture, pour le d�boguage par exemple.

Voil�, j'esp�re que cette documentation sera utile � d'autres personnes qu'� moi. Si vous aviez des commentaires, pour signaler tous types d'erreurs, pour des compl�ments, des encouragements,... �crivez-moi � [email protected]. Ce texte a �t� r�dig� par Philippe Beaumont, de la soci�t� Nexedi le 03 avril 2002.

*This document was written by Philippe Beaumont and is (c) Nexedi SARL . It can be redistributed under GFDL license*