doc/devmanual_fr/chap_fondements_cubicweb.txt
changeset 50 d642f43eb87d
parent 48 0adf4d507ede
child 51 8c5de7159cab
equal deleted inserted replaced
48:0adf4d507ede 50:d642f43eb87d
     1 .. -*- coding: utf-8 -*-
       
     2 
       
     3 Fondements `CubicWeb`
       
     4 =====================
       
     5 
       
     6 Architecture globale
       
     7 --------------------
       
     8 .. image:: archi_globale.png
       
     9 
       
    10 **Note**: en pratique la partie cliente et la partie serveur sont
       
    11 généralement intégrées dans le même processus et communiquent donc
       
    12 directement, sans nécessiter des appels distants via Pyro. Il est
       
    13 cependant important de retenir que ces deux parties sont disjointes
       
    14 et qu'il est même possible d'en exécuter plusieurs exemplaires dans
       
    15 des processus distincts pour répartir la charge globale d'un site
       
    16 sur une ou plusieurs machines.
       
    17 
       
    18 Concepts et vocabulaire
       
    19 -----------------------
       
    20 
       
    21 *schéma*
       
    22   le schéma définit le modèle de données d'une application sous forme
       
    23   d'entités et de relations, grâce à la biblithèque `yams`_. C'est
       
    24   l'élément central d'une application. Il est initialement défini sur
       
    25   le système de fichiers et est stocké dans la base de données lors de
       
    26   la création d'une instance. `CubicWeb` fournit un certain nombres de
       
    27   types d'entités inclus systématiquement car nécessaire au noyau
       
    28   `CubicWeb` et une librairie de cubes devant être inclus
       
    29   explicitement le cas échéant.
       
    30 
       
    31 *type d'entité* 
       
    32   une entité est un ensemble d'attributs ; l'attribut de
       
    33   base de toute entité, qui est sa clef, est l'eid
       
    34 
       
    35 *type de relation*
       
    36   les entités sont liées entre elles par des relations. Dans `CubicWeb` 
       
    37   les relations sont binaires : par convention on nomme le premier terme
       
    38   d'une relation son 'sujet' et le second son 'objet'.
       
    39 
       
    40 *type d'entité final*
       
    41   les types finaux correspondent aux types de bases comme les chaînes
       
    42   de caractères, les nombres entiers... Une propriété de ces types est
       
    43   qu'ils ne peuvent être utilisés qu'uniquement comme objet d'une
       
    44   relation. Les attributs d'une entité (non finale) sont des entités
       
    45   (finales).
       
    46 
       
    47 *type de relation finale*
       
    48   une relation est dite finale si son objet est un type final. Cela revient à
       
    49   un attribut d'une entité.
       
    50 
       
    51 *entrepôt*
       
    52   ou *repository*, c'est la partie serveur RQL de `CubicWeb`. Attention à ne pas
       
    53   confondre avec un entrepôt mercurial ou encore un entrepôt debian.
       
    54 
       
    55 *source*
       
    56   une source de données est un conteneur de données quelquonque (SGBD, annuaire
       
    57   LDAP...) intégré par l'entrepôt `CubicWeb`. Un entrepôt possède au moins une source
       
    58   dite "system" contenant le schéma de l'application, l'index plein-texte et
       
    59   d'autres informations vitales au système.
       
    60 
       
    61 *configuration*
       
    62   il est possible de créer différentes configurations pour une instance :
       
    63 
       
    64   - ``repository`` : entrepôt uniquement, accessible pour les clients via Pyro
       
    65   - ``twisted`` : interface web uniquement, communiquant avec un entrepôt via Pyro
       
    66   - ``all-in-one`` : interface web et entrepôt dans un seul processus. L'entrepôt
       
    67      peut ou non être accessible via Pyro
       
    68 
       
    69 *cube*
       
    70   un cube est un modèle regroupant un ou plusieurs types de données et/ou
       
    71   des vues afin de fournir une fonctionalité précise, ou une application `CubicWeb`
       
    72   complète utilisant éventuellement d'autres cube. Les différents
       
    73   cubes disponibles sur une machine sont installés dans
       
    74   `/path/to/forest/cubicweb/cubes`
       
    75 
       
    76 *instance*
       
    77   une instance est une installation spécifique d'un template. Sont regroupes
       
    78   dans une instance tous les fichiers de configurations necessaires au bon
       
    79   fonctionnement de votre application web. Elle referrera au(x) cube(s) sur 
       
    80   le(s)quel(s) votre application se base.
       
    81   Par exemple intranet/jpl et logilab.org sont deux instances du cube jpl que
       
    82   nous avons developpes en interne. 
       
    83   Les instances sont définies dans le répertoire `~/etc/cubicweb.d`.
       
    84 
       
    85 *application*
       
    86   le mot application est utilisé parfois pour parler d'une instance et parfois
       
    87   d'un composant, en fonction du contexte... Mieux vaut donc éviter de
       
    88   l'utiliser et utiliser plutôt *cube* et *instance*.
       
    89 
       
    90 *result set*
       
    91   objet encaspulant les résultats d'une requête RQL et des informations sur
       
    92   cette requête.
       
    93 
       
    94 *Pyro*
       
    95   `Python Remote Object`_, système d'objets distribués pur Python similaire à
       
    96   Java's RMI (Remote Method Invocation), pouvant être utilisé pour la
       
    97   communication entre la partie web du framework et l'entrepôt RQL.
       
    98 
       
    99 .. _`Python Remote Object`: http://pyro.sourceforge.net/
       
   100 .. _`yams`: http://www.logilab.org/project/name/yams/
       
   101 
       
   102 
       
   103 Moteur `CubicWeb`
       
   104 -----------------
       
   105 
       
   106 Le moteur web de `CubicWeb` consiste en quelques classes gérant un ensemble d'objets
       
   107 chargés dynamiquement au lancement de `CubicWeb`. Ce sont ces objets dynamiques, issus
       
   108 du modèle ou de la librairie, qui construisent le site web final. Les différents
       
   109 composants dynamiques sont par exemple : 
       
   110 
       
   111 * coté client et serveur
       
   112 
       
   113  - les définitions d'entités, contenant la logique permettant la manipulation des
       
   114    données de l'application
       
   115 
       
   116 * coté client
       
   117 
       
   118   - les *vues* , ou encore plus spécifiquement 
       
   119 
       
   120     - les boites
       
   121     - l'en-tête et le pied de page
       
   122     - les formulaires
       
   123     - les gabarits de pages
       
   124 
       
   125   - les *actions*
       
   126   - les *controleurs*
       
   127 
       
   128 * coté serveur
       
   129 
       
   130   - les crochets de notification
       
   131   - les vues de notification
       
   132 
       
   133 Les différents composants du moteur sont :
       
   134 
       
   135 * un frontal web (seul twisted disponible pour le moment), transparent du point
       
   136   de vue des objets dynamiques
       
   137 * un objet encapsulant la configuration
       
   138 * un `vregistry` (`cubicweb.cwvreg`) contenant les objets chargés dynamiquements
       
   139 
       
   140 Détail de la procédure d'enregistrement
       
   141 ---------------------------------------
       
   142 Au démarage le `vregistry` ou base de registres inspecte un certain nombre de
       
   143 répertoires à la recherche de définition de classes "compatible". Après une
       
   144 procédure d'enregistrement les objets sont affectés dans différents registres
       
   145 afin d'être ensuite séléctionné dynamiquement pendant le fonctionnement de
       
   146 l'application.
       
   147 
       
   148 La classe de base de tout ces objets est la classe `AppRsetObject` (module
       
   149 `cubicweb.common.appobject`). 
       
   150 
       
   151 
       
   152 API Python/RQL
       
   153 --------------
       
   154 
       
   155 Inspiré de la db-api standard, avec un object Connection possédant les méthodes
       
   156 cursor, rollback et commit principalement. La méthode importante est la méthode
       
   157 `execute` du curseur :
       
   158 
       
   159 `execute(rqlstring, args=None, eid_key=None, build_descr=True)`
       
   160 
       
   161 :rqlstring: la requête rql à éxécuter (unicode)
       
   162 :args: si la requête contient des substitutions, un dictionnaire contenant les
       
   163        valeurs à utiliser
       
   164 :eid_key: 
       
   165    un détail d'implémentation du cache de requêtes RQL fait que si une substitution est
       
   166    utilisée pour introduire un eid *levant des ambiguités dans la résolution de
       
   167    type de la requête*, il faut spécifier par cet argument la clé correspondante
       
   168    dans le dictionnaire
       
   169 
       
   170 C'est l'objet Connection qui possède les méthodes classiques `commit` et
       
   171 `rollback`. Vous ne *devriez jamais avoir à les utiliser* lors du développement
       
   172 d'interface web sur la base du framework `CubicWeb` étant donné que la fin de la
       
   173 transaction est déterminée par celui-ci en fonction du succès d'éxécution de la
       
   174 requête. 
       
   175 
       
   176 NOTE : lors de l'éxécution de requêtes de modification (SET,INSERT,DELETE), si une
       
   177 requête génère une erreur liée à la sécurité, un rollback est systématiquement
       
   178 effectuée sur la transaction courante.
       
   179 
       
   180 
       
   181 La classe `Request` (`cubicweb.web`)
       
   182 ------------------------------------
       
   183 Une instance de requête est créée lorsque une requête HTTP est transmise au
       
   184 serveur web. Elle contient des informations telles que les paramètres de
       
   185 formulaires, l'utilisateur connecté, etc. 
       
   186 
       
   187 **De manière plus générale une requête représente une demande d'un utilisateur,
       
   188 que se soit par HTTP ou non (on parle également de requête rql coté serveur par
       
   189 exemple)**
       
   190 
       
   191 Une instance de la classe `Request` possède les attributs :
       
   192 
       
   193 * `user`, instance de`cubicweb.common.utils.User` correspondant à l'utilisateur
       
   194   connecté 
       
   195 * `form`, dictionaire contenant les valeurs de formulaire web
       
   196 * `encoding`, l'encodage de caractère à utiliser dans la réponse
       
   197 
       
   198 Mais encore :
       
   199 
       
   200 :Gestion des données de session:        
       
   201   * `session_data()`, retourne un dictionaire contenant l'intégralité des
       
   202     données de la session
       
   203   * `get_session_data(key, default=None)`, retourne la valeur associée à
       
   204     la clé ou la valeur `default` si la clé n'est pas définie
       
   205   * `set_session_data(key, value)`, associe une valeur à une clé
       
   206   * `del_session_data(key)`,  supprime la valeur associé à une clé
       
   207     
       
   208 
       
   209 :Gestion de cookie:
       
   210   * `get_cookie()`, retourne un dictionnaire contenant la valeur de l'entête
       
   211     HTTP 'Cookie'
       
   212   * `set_cookie(cookie, key, maxage=300)`, ajoute un en-tête HTTP `Set-Cookie`,
       
   213     avec une durée de vie 5 minutes par défault (`maxage` = None donne un cooke
       
   214     *de session"* expirant quand l'utilisateur ferme son navigateur
       
   215   * `remove_cookie(cookie, key)`, fait expirer une valeur
       
   216 
       
   217 :Gestion d'URL:
       
   218   * `url()`, retourne l'url complète de la requête HTTP
       
   219   * `base_url()`, retourne l'url de la racine de l'application
       
   220   * `relative_path()`, retourne chemin relatif de la requête
       
   221 
       
   222 :Et encore...:
       
   223   * `set_content_type(content_type, filename=None)`, place l'en-tête HTTP
       
   224     'Content-Type'
       
   225   * `get_header(header)`, retourne la valeur associé à un en-tête HTTP
       
   226     arbitraire de la requête
       
   227   * `set_header(header, value)`, ajoute un en-tête HTTP arbitraire dans la
       
   228     réponse 
       
   229   * `cursor()` retourne un curseur RQL sur la session
       
   230   * `execute(*args, **kwargs)`, raccourci vers .cursor().execute()
       
   231   * `property_value(key)`, gestion des propriétés (`EProperty`)
       
   232   * le dictionaire `data` pour stocker des données pour partager de
       
   233     l'information entre les composants *durant l'éxécution de la requête*.
       
   234 
       
   235 A noter que cette classe est en réalité abstraite et qu'une implémentation
       
   236 concrète sera fournie par le *frontend* web utilisé (en l'occurent *twisted*
       
   237 aujourd'hui). Enfin pour les vues ou autres qui sont éxécutés coté serveur,
       
   238 la majeure partie de l'interface de `Request` est définie sur la session
       
   239 associée au client. 
       
   240 
       
   241 
       
   242 La classe `AppObject`
       
   243 ---------------------
       
   244 
       
   245 En général :
       
   246 
       
   247 * on n'hérite pas directement des cette classe mais plutôt d'une classe
       
   248   plus spécifique comme par exemple `AnyEntity`, `EntityView`, `AnyRsetView`,
       
   249   `Action`...
       
   250 
       
   251 * pour être enregistrable, un classe fille doit définir son registre (attribut
       
   252   `__registry__`) et son identifiant (attribut `id`). Généralement on n'a pas à
       
   253   s'occuper du registre, uniquement de l'identifiant `id` :) 
       
   254 
       
   255 On trouve un certain nombre d'attributs et de méthodes définis dans cette classe
       
   256 et donc commune à tous les objets de l'application :
       
   257 
       
   258 A l'enregistrement, les attributs suivants sont ajoutés dynamiquement aux
       
   259 *classes* filles:
       
   260 
       
   261 * `vreg`, le `vregistry` de l'application
       
   262 * `schema`, le schéma de l'application
       
   263 * `config`, la configuration de l'application
       
   264 
       
   265 On trouve également sur les instances les attributs :
       
   266 
       
   267 * `req`, instance de `Request`
       
   268 * `rset`, le "result set" associé à l'objet le cas échéant
       
   269 * `cursor`, curseur rql sur la session
       
   270 
       
   271 
       
   272 :Gestion d'URL:
       
   273   * `build_url(method=None, **kwargs)`, retourne une URL absolue construites à
       
   274     partir des arguments donnés. Le *controleur* devant gérer la réponse
       
   275     peut-être spécifié via l'argument spécial `method` (le branchement est
       
   276     théoriquement bien effectué automatiquement :).
       
   277 
       
   278   * `datadir_url()`, retourne l'url du répertoire de données de l'application
       
   279     (contenant les fichiers statiques tels que les images, css, js...)
       
   280 
       
   281   * `base_url()`, raccourci sur `req.base_url()`
       
   282 
       
   283   * `url_quote(value)`, version *unicode safe* de de la fonction `urllib.quote`
       
   284 
       
   285 :Manipulation de données:
       
   286 
       
   287   * `etype_rset(etype, size=1)`, raccourci vers `vreg.etype_rset()`
       
   288 
       
   289   * `eid_rset(eid, rql=None, descr=True)`, retourne un objet result set pour
       
   290     l'eid donné
       
   291   * `entity(row, col=0)`, retourne l'entité correspondant à la position données
       
   292     du "result set" associé à l'objet
       
   293 
       
   294   * `complete_entity(row, col=0, skip_bytes=True)`, équivalent à `entity` mais
       
   295     appelle également la méthode `complete()` sur l'entité avant de la retourner
       
   296 
       
   297 :Formattage de données:
       
   298   * `format_date(date, date_format=None, time=False)`
       
   299   * `format_time(time)`,
       
   300 
       
   301 :Et encore...:
       
   302 
       
   303   * `external_resource(rid, default=_MARKER)`, accède à une valeur définie dans
       
   304     le fichier de configuration `external_resource`
       
   305     
       
   306   * `tal_render(template, variables)`, 
       
   307 
       
   308 
       
   309 **NOTE IMPORTANTE**
       
   310 Lorsqu'on hérite d'`AppObject` (même indirectement), il faut **toujours**
       
   311 utiliser **super()** pour récupérer les méthodes et attributs des classes
       
   312 parentes, et pas passer par l'identifiant de classe parente directement.
       
   313 (sous peine de tomber sur des bugs bizarres lors du rechargement automatique
       
   314 des vues). Par exemple, plutôt que d'écrire::
       
   315 
       
   316       class Truc(PrimaryView):
       
   317           def f(self, arg1):
       
   318               PrimaryView.f(self, arg1)
       
   319 
       
   320 Il faut écrire::
       
   321       
       
   322       class Truc(PrimaryView):
       
   323           def f(self, arg1):
       
   324               super(Truc, self).f(arg1)
       
   325 
       
   326 
       
   327 
       
   328 
       
   329 
       
   330 
       
   331 Structure standard d'un cube
       
   332 ----------------------------
       
   333 
       
   334 Un cube complexe est structuré selon le modèle suivant :
       
   335 
       
   336 ::
       
   337   
       
   338   moncube/
       
   339   |
       
   340   |-- schema.py
       
   341   |
       
   342   |-- entities/
       
   343   |
       
   344   |-- sobjects/
       
   345   |
       
   346   |-- views/
       
   347   |
       
   348   |-- test/
       
   349   |
       
   350   |-- i18n/
       
   351   |
       
   352   |-- data/
       
   353   |
       
   354   |-- migration/
       
   355   | |- postcreate.py
       
   356   | \- depends.map
       
   357   |
       
   358   |-- debian/
       
   359   |
       
   360   \-- __pkginfo__.py
       
   361     
       
   362 On peut utiliser de simple module python plutôt que des répertoires (packages),
       
   363 par ex.:
       
   364 
       
   365 ::
       
   366   
       
   367   moncube/
       
   368   |
       
   369   |-- entities.py
       
   370   |-- hooks.py
       
   371   \-- views.py
       
   372     
       
   373 
       
   374 où :
       
   375 
       
   376 * ``schema`` contient la définition du schéma (coté serveur uniquement)
       
   377 * ``entities`` contient les définitions d'entités (coté serveur et interface web)
       
   378 * ``sobjects`` contient les crochets et/ou vues de notification (coté serveur
       
   379   uniquement) 
       
   380 * ``views`` contient les différents composants de l'interface web (coté interface
       
   381   web uniquement)  
       
   382 * ``test`` contient les tests spécifiques à l'application (non installé)
       
   383 * ``i18n`` contient les catalogues de messages pour les langues supportées (coté
       
   384   serveur et interface web) 
       
   385 * ``data`` contient des fichiers de données arbitraires servis statiquement
       
   386   (images, css, fichiers javascripts)... (coté interface web uniquement)
       
   387 * ``migration`` contient le fichier d'initialisation de nouvelles instances
       
   388   (``postcreate.py``) et générallement un fichier donnant les dépendances `CubicWeb` du
       
   389   composant en fonction de la version de celui-ci (``depends.map``)
       
   390 * ``debian`` contient les fichiers contrôlant le packaging debian (vous y
       
   391   trouverez les fichiers classiques ``control``, ``rules``, ``changelog``... (non
       
   392   installé) 
       
   393 * le fichier ``__pkginfo__.py`` donne un certain nombre de méta-données sur le
       
   394   composant, notamment le nom de la distribution et la version courante (coté
       
   395   serveur et interface web) ou encore les sous-cubes utilisés par ce
       
   396   cube. 
       
   397 
       
   398 Le strict minimum étant :
       
   399 
       
   400 * le fichier ``__pkginfo__.py``
       
   401 * la définition du schéma