Magazine Internet

Python et Webpy : partie 1

Publié le 30 septembre 2009 par Mikebrant

whao, ca faisait longtemps

Le début d'une longue série de billets sur webpy  qui a pour but de créer un site d'upload de fichiers.
Mais ce sera un site basique,  sans css, etc. :
   L'utilisateur s'authentifie, il choisit son fichier et l'uploade sur un serveur qui vérifie, via SOAP, si ce dernier a le droit d'uploader, et c'est tout.
   Niveau modules utilisés : gettext pour l'i18n, beaker pour les sessions et soaplib pour le SOAP.
   Pour le SQL, ce sera MySQL.
   Pour le serveur Web, ce sera Apache avec son mod_wsgi(d'ailleurs le seul truc vraiment pourri avec le mod_wsgi c'est que les erreurs se mettent dans les logs d'Apache...).

Pourquoi webpy ?
Parce qu'il est super léger et que je le trouve formi, formidable.

Dans ce premier billet on va juste mettre en place l'architecture et la configuration de notre plateforme.

Il faut commencer par créer tous nos dossiers/fichiers pour que l'arborescende de notre code donne ça :

arbo

Et là, vous vous dites : "c'est quoi cette architecture ?"
Non ? personne ne se pose la question ?
Et bien je vais quand même répondre.
web va contenir tout le code lié à notre site web, d'où son nom.
files va contenir tout le code lié à nos serveurs de stockage, qui vont vérifier si l'utilisateur a le droit d'uploader ou pas.
models va contenir tous nos modèles, il sera donc partagé entre files et web.
soap va contenir tout le code lié au SOAP, il sera lui aussi partagé entre files et web.
utils va contenir tout notre code tierce, et lui aussi sera partagé entre files et web.

Voilà, l'arborescende du code est faite.
Place à la création de la bdd et de ses tables.
Pour créer la bdd, rien de plus simple :

create database quisaura character set uft8;


puis l'utilisateur à partir duquel on va se connecter à la bdd :

Grant all privileges on quisaura.* TO'quisaura'@'localhost' identified by'123456';
flush privileges;

Voilà. Pour les tables, voici le sql :

CREATE TABLE `quisaura`.`utilisateur` (
`idUser` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`login` VARCHAR( 255 ) NOT NULL ,
`password` INT( 255 ) NOT NULL ,
`bloque` BOOL NOT NULL DEFAULT '0'
) ENGINE = InnoDB;
CREATE TABLE `quisaura`.`contenu` (
`idContenu` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`idCreateur` INT NOT NULL ,
`nomFichier` VARCHAR( 255 ) NOT NULL ,
`url` VARCHAR( 255 ) NOT NULL ,
`bloque` BOOL NOT NULL DEFAULT '0'
) ENGINE = InnoDB ;
CREATE TABLE `quisaura`.`serveur` (
`idServeur` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`nom` VARCHAR( 255 ) NOT NULL
) ENGINE = InnoDB;
CREATE TABLE `quisaura`.`contenuServeur` (
`idContenuServeur` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`idContenu` INT NOT NULL ,
`idServeur` INT NOT NULL ,
`url` VARCHAR( 255 ) NOT NULL
) ENGINE = InnoDB;

Et c'est fini pour le SQL(qui changera peut-être).

Maintenant place à la conf. d'apache (assurez-vous d'avoir bien installé mod_wsgi).

donc dans le fichier de conf d'apache, il faut rajouter ceci (sur une fedora/centos : /etc/httpd/conf/httpd.conf)  :

###qui-saura : général
AddType text/html .wsgi
WSGIRestrictStdout Off
WSGIPythonPath "/var/www/html/qui-saura"
#partie web
<VirtualHost *:80>
   ServerName qui-saura
   WSGIScriptAlias / /var/www/html/qui-saura/web/appli.wsgi
   <Directory /var/www/html/qui-saura/web/>
   Order deny,allow
   Allow from all
   </Directory>
</VirtualHost>
#partie files
<VirtualHost *:80>
   ServerName files1.qui-saura
   WSGIScriptAlias / /var/www/html/qui-saura/files/appli.wsgi
   <Directory /var/www/html/qui-saura/web/>
   Order deny,allow
   Allow from all
   </Directory>
</VirtualHost>


Alors :
La 1° ligne va permettre à nos vues de s'afficher.
Après, c'est pour que l'on puisse faire un print() .
Et le WSGIPythonPath équivaut au PythonPath : pour accéder à un module x de models on tappera : from models import x

Après on crée 2 virtualHosts : un pour la partie web et l'autre pour représenter un serveur de stockage.
L'attribut WSGIScriptAlias pointe sur le fichier représentant notre application WSGI.

La configuration d'apache est finie et donc il faut créer les deux fichiers appli.wsgi dans web/ et files/(pensez à éditer le fichier hosts).

Normalement, nous devons dans nos appli.wsgi déclarer à la main nos urls ainsi que les actions vers lesquelles elles pointent.
Mais c'est pas top top, alors on va s'inspirer de la méthode décrite sur le site de webpy (malheureusement je ne retrouve plus le lien pointant directement sur la méthode).
Cette méthode consiste à :
 - créer un attribut de classe url dans chaque action, de type list/tuple,
 - affecter une métaclasse à toutes nos actions
 - créer un dossier controleurs qui va contenir nos actions.
 - depuis le appli.wsgi, lister ce dossier et importer via __import__ toutes nos actions
 - de ce fait, lors de l'import la métaclasse va être appelée et va créer une liste contenant les urls et les actions liées.
Compris ? au pire c'est pas grave, un bout de code vaut parfois mille mots.

On va donc créer notre métaclasse et la fonction qui liste le dossier controleurs. Pour ce faire, créez dans utils/ le fichier coeur.py et mettez-lui ça :

#-*- coding:utf-8 -*-
import os
urls = [] # la liste des urls/controleurs
class MetaClass(type):
   """ La MetaClasse, à partir de laquelle on va automatiquement lier une classe à une vue.
   Une Méta Classe doit hériter de type.
   """
   def __init__(cls, nom, parents, dico):
   """
   cls   : la classe [ qui va avoir comme méta classe celle-ci ]
   nom   : le nom de la classe
   parents : les parents de la classe
   dico   : méthodes&attributs de la classe
   """
   if(isinstance(dico["url"],(str,unicode))):
   urls.append(dico["url"])
   urls.append("%s.%s" % (cls.__module__, nom))
   else:
   for url in dico["url"]:
   urls.append(url)
   urls.append("%s.%s" % (cls.__module__, nom))
def listeControleurs(dossierControleurs, moduleControleurs):
   """
   dossierControleurs : indique le dossier où se trouvent les controlleurs
   moduleControleurs : le module des controlleurs
   """
   for obj in os.listdir(dossierControleurs):#on liste les fichiers du dossier
   if obj.endswith('Controleur.py'):#si le fichier finit par 'Controleur.py'
   nomModule = moduleControleurs + '.' + obj[:-3]
   __import__(nomModule)#on importe


Voilà, j'explique pas il y a les commentaires, qui je pense, devrait suffir. Sinon dites-le.
(Notez bien que les fichiers contenant les actions doivent se terminer par Controleur.py)

Il nous faut maintenant créer deux fichiers conf.py dans web/ et files/, qui vont contenir toutes les petites configurations de nos applis(comme le chemin vers le dossier controleurs) :

# -*- coding:utf-8 -*-
import os
CHEMIN = os.path.dirname(__file__) # Dossier racine de notre application
MODULE_CONTROLEURS = 'controleurs'#nom du dossier contenant nos actions
CHEMIN_CONTROLEURS = CHEMIN + '/' + MODULE_CONTROLEURS + '/'#chemin vers le dossier controleurs
CHEMIN_VUES = CHEMIN + '/vues/' #chemin vers les vues
CHEMIN_SESSION = CHEMIN + '/sessions/'#le chemin vers les sessions
CHEMIN_I18N = CHEMIN + '/i18n'#le chemin vers l'i18n


Maintenant on crée les dossiers controleurs (avec les __init__.py) dans web/ et files/  ainsi que les dossiers vues (qui vont contenir nos vues) toujours dans web/ et files/ .
Et place à l'i18n, il va falloir tapper quelques commandes : (changez les chemins, et pensez aussi à y faire pour files/ )


$ cd /var/www/html/qui-saura/web/
$ mkdir -p i18n/en_US/LC_MESSAGES i18n/fr_FR/LC_MESSAGES
$ touch vues/test.html & /chemin/vers/pygettext.py -a -v -d messages -o i18n/messages.po vues/*.html & rm vues/test.html


Maintenant éditez le fichier i18n/messages.po et ligne 13, remplacez CHARSET par utf-8.

$ cp i18n/messages.po i18n/en_US/LC_MESSAGES/ & cp i18n/messages.po i18n/fr_FR/LC_MESSAGES/
$
msgfmt -o i18n/en_US/LC_MESSAGES/messages.mo i18n/en_US/LC_MESSAGES/messages.po
$
msgfmt -o i18n/fr_FR/LC_MESSAGES/messages.mo i18n/fr_FR/LC_MESSAGES/messages.po

Ca y est ! l'i18n est mis en place , c'est pas merveilleux ?

Et enfin le voilà ! Le code de nos fameux appli.wsgi :

#-*- coding:utf-8 -*-
import sys,os
sys.path.append(os.path.dirname(__file__))
from beaker.middleware import SessionMiddleware
from conf import *
from utils.coeur import *
import gettext
import web
web.config.debug = False#pour que les sessions marchent
# Mapping URL => Controleur
listeControleurs(CHEMIN_CONTROLEURS, MODULE_CONTROLEURS)
# Configuration des sessions
sessionOptions = {
   'session.key' : 'SID',   # Nom du cookie contenant l'id de la session
   'session.data_dir' : CHEMIN_SESSION,   # Repertoire ou sont stocké les sessions sur disque
   'session.auto' : True   # Sauvegarde automatique des sessions
}
#l'i18n
gettext.install('messages',CHEMIN_I18N,unicode=True)
gettext.translation('messages',CHEMIN_I18N,languages=['fr_FR','en_US']).install(True)
app = web.application(urls)
app.internalerror = web.debugerror # Affichage des message d'erreurs
application = SessionMiddleware(app.wsgifunc(),sessionOptions) # Gestion des sessions


Voilà, voilà, je n'explique pas non plus, il y a les commentaires qui devraient suffire, non ?

Maintenant si vous essayez d'accéder à http://qui-saura/ ou http://files1.qui-saura/ vous verrez "not found".
Cela veut dire aussi que ce premier billet est terminé et qu'il ne nous reste plus qu'à créer nos actions.
À bientôt pour le 2° billet.
PS : vous ne connaitriez pas de dotclear/wordpress écrit en python ?


Vous pourriez être intéressé par :

Retour à La Une de Logo Paperblog

Ces articles peuvent vous intéresser :

A propos de l’auteur


Mikebrant 9 partages Voir son profil
Voir son blog

l'auteur n'a pas encore renseigné son compte l'auteur n'a pas encore renseigné son compte l'auteur n'a pas encore renseigné son compte