mercredi 26 août 2015

Tutoriel Flask - Un serveur web python - Comment créer un 1er site dynamique

 L'objectif de ce tuto est de parcourir rapidement les possibilités du serveur d'application python flask, nous allons voir comment créer simplément une première page puis une page dynamique avec le moteur de template, inclure bootstrap et créer une barre de navigation



Vous pouvez télécharger l'ensemble du code sur GitHub

La vidéo du tutoriel :

A) Installation
B) Structure de l'application
C) Une première requête
D) Les templates
E) Template et héritage
F)  BootStrap
G) La barre de navigation

A)Virtualenv et pip

La 1ere étape est de créer un environnement spécifique pour isoler nos modules python. On utilise virtualenv (si vous ne connaissez pas la technique, je vous renvoie à l'article de SAM et MAX)


Puis on active l'environnement :


Une fois l'environnement activé, on installe les modules pythons nécessaires pour le serveur web.
Pour ce faire je crée un fichier requierements.txt avec les modules que j'utilise fréquemment :
Flask
Flask-restful
Flask-bootstrap
Flask-Nav<
Flask-script 
Flask install le module du serveur d'application
Flask-restful est le module qui facilite l'utilisation du protocole REST (utile pour créer des APIs)
Flask-Bootstrap facilite l'utilisation de bootstrap
Flask-Nav facilite la création d'une barre de navigation
Flask-script va gérer le démarrage du serveur
Puis on exécute la commande :
pip install -r requierements.txt    
cette commande va télécharger les modules et les installer dans votre environnement.

B) Structure de l'application :

Voici la structure du projet :

  • FalskTutorielBase est le nom du projet et le répertoire le plus élevé
  • app, contiendra notre application web
  • static par convention dans flask contiendra les fichiers static (css, js, image)
  • template par convention dans flask contiendra les pages web dynamique générer par le moteur Jinja2
  • venv est le répertoire contenant l'environnement virtuel que vous avez créé à l'étape précédente.

C) faire un 1er test bête :

Créer à la racine le fichier suivant, je l'ai appelé 1test.py. Ce fichier est issue du site de flask, je ne fais que vous le commentez :
from flask import Flask
#déclare le serveur flask
app = Flask(__name__)
#crée la route web de la racine du site
#et la lie à la fonction hello
@app.route("/")
def hello():
   return "Hello World!"
if __name__ == "__main__":
#lance le serveur Flask
   app.run()
On le lance avec la commande python 1test.py

Voici le résultat dans un navigateur :

Comme vous le remarquez le serveur est créé sur l'interface réseau loopback donc il n'est pas accessible depuis un autre ordinateur.
Nous allons voir comme simplement permettre le lancement du serveur via le code ou une ligne de commande.
Via le code il suffit de changer la ligne comme suit :

app.run(host="0.0.0.0",debug=True)
La commande run permet de définir sur quelle interface et quel port tourne le serveur flask. J’ai aussi activé la fonction debug qui permet de recharger le serveur automatiquement et qui renvoie les exceptions pythons dans le navigateur. La documentation complète se trouve sur cette page.
Définir ces paramètres dans le code n'est pas très pratique, surtout si vous avez un serveur de production (chez un hébergeur et serveur de dév sur votre machine à chaque déploiement vous devez changer le code).
C'est là que Flask-script entre en action, il permet de passer des paramètres au lancement de notre script python. Voici le code qui se trouve dans le fichier 2test.py :
from flask import Flask
from flask.ext.script import Manager

#déclare le serveur flask
app = Flask(__name__)
#déclare le plug-in flask-script
manager = Manager( app)

#crée la route web de la racine du site
#et la lie à la fonction hello
@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
   #lance le serveur Flask via le plug-in flask-script
   manager.run()

Maintenant dans la console si on tape :
python 2test.py runserver --help


pour lancer le server Flask il suffit de taper :
python 2test.py runserver -h 0.0.0.0 -d

D) Les templates:

Le Serveur est lancé, passons aux pages web dynamiques (côtés serveur) grâce au moteur de template Jinja2.
Par convention vos pages templates doivent se trouver dans le répertoire template que nous avons précédemment créé.
Nous allons créer deux fichiers : un premier fichier contenant le serveur et les routes, un autre contenant le template.
Voyons le fichier hello.html qui se trouve dans le répertoire template :
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
    <h1>Hello {{ name }}!</h1>
{% else %}
   <h1>Hello World!</h1>
{% endif %} 

Le moteur de template va prendre les instructions qui sont dans les {{ }} ou {% %}
les variables sont entre {{ <variable> }} (elles sont passées via la fonction de rendu,comme on le verra plus loin) et les structures de contrôle entre {% %}
Le reste est un fichier HTML classique

Le fichier serveur 3test.py :
from flask import Flask,render_template,request
from flask.ext.script import Manager

#déclare le serveur flask
app = Flask(__name__)

#déclare le plug-in flask-script
manager = Manager( app)

#crée la route web de la racine du site
#et la lie à la fonction index
@app.route("/")
def index():
    return "ceci est la page index"

#on crée la nouvelle route et on la lie à fonction Hello
@app.route('/hello')
@app.route('/hello/<name>')
def hello(name=None):
    if name == None :
       #Si le nom n'est pas dans l'url, je tente de l'extraire depuis la requête
       name = request.args.get('name','default')
       return render_template('hello.html', name=name)

if __name__ == "__main__":
   #lance le serveur Flask via le plug-in flask-script
   manager.run()

On importe en ligne 1 render_template.
Puis on a défini deux nouvelles routes :
@app.route('/hello/')
@app.route('/hello/<name>')       

Une route simple hello, puis une route avec une variable name, qui sera automatiquement passé à notre fonction
Regardons maintenant la fonction hello :
def hello(name=None):
    if name == None :
       #Si le nom n'est pas dans l'url, je tente de l'extraire depuis la requête
       name = request.args.get('name','default')
       return render_template('hello.html', name=name)
     
 
la commande :
return render_template('hello.html', name=name)
lance le rendu du template et le renvoie. On passe en paramètre name au template.

Flask utilise l'objet request standard de Urllib, la commande :
request.args.get('name',None)
permet de récupérer le paramètre name de l'url et de le mettre à None si il n'existe pas
Voici le résultat :




Voilà pour un premier aperçu pour des templates, la doc complète du moteur de rendu se trouve ici : http://jinja.pocoo.org/docs/dev/templates/

E) Template et héritage

Avant de comprendre comment nous allons utiliser bootstrap, je vais détailler une fonctionnalité de Jinja : l'héritage.
Il est possible de définir une page de base avec des blocs et de faire hériter d'autre page de celle-ci.
Nous allons définir une page de base dans laquelle nous allons mettre une CSS et des blocs : entête,titre contenu et footer qui définira un bas de page commun.
Appelons-la : MyBase.html
<html>
<head>
 <link rel="stylesheet" href="{{url_for('static', filename ='css/Base.css', _external = True)}}" />
   {% block head %}
     <title>{% block title %}{% endblock %} - My Application </title>
   {% endblock %}
</head>

<body>
  {% block body %} {% endblock %}
  <div class="footer"> ceci est footer commun </div>
</body>
</html>     
On voit clairement les blocs tel que {% block body %} {% endblock %}.
On remarquera le lien vers la stylesheet :
<link rel="stylesheet" href="{{url_for('static', filename ='css/Base.css', _external = True)}}" />    
La stylesheet étant un fichier static, elle dans le répertoire static on passe par la fonction url_for pour avoir son lien.
Voyons maintenant la page qui hérite de ma base :
{% extends "MyBase.html" %}
{% block title %} Hello2{% endblock %}
{% block body %}
{% if name %}
    <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello World!</h1>
{% endif %}
{% endblock %}     
La commande extends permet d'hériter et je n'ai plus qu'à redéfinir mes blocs, rien de compliqué.
Le fichier serveur n'a rien de compliqué, je vous laisse le découvrir sur le GitHub : 4test.py
Voici le résultat :

F) Utilisation de bootStrap :

L'extension Flask-bootstrap permet de bénéficier d'un template qui inclut Bootstrap. On peut s'en passer mais celui-ci simplifie l'emploie de bootStrap donc pourquoi s'en priver.
Ce template défini les blocs suivant :
Block name
Outer Block
Purpose
doc
Outermost block.
html
doc
Contains the complete content of the <html>  tag.
html_attribs
doc
Attributes for the HTML tag.
head
doc
Contains the complete content of the <head>  tag.
body
doc
Contains the complete content of the <body>  tag.
body_attribs
body
Attributes for the Body Tag.
title
head
Contains the complete content of the <title>  tag.
styles
head
Contains all CSS style  <link> tags inside head.
metas
head
Contains all  <meta> tags inside head.
navbar
body
An empty block directly above  content.
content
body
Convenience block inside the body. Put stuff here.
scripts
body
Contains all  <script> tags at the end of the body.


Reprenons notre projet pour inclure bootstrap, il suffit de suivre deux étapes - les fichiers pour cette étapes seront Mybase2.html,hello3.html et 5test.py:

1) Changer la page de base : Mybase2.html


{% extends "bootstrap/base.html" %}

{% block styles %}
  {{super()}}
  <link rel="stylesheet" href="{{url_for('static', filename ='css/Base.css', _external = True)}}" />
{% endblock %}

{% block body %}
   {{super()}}
   <div class="footer">ceci est footer commun</div>
{% endblock %}
La structure de base HTML se trouve dans la page base.html défini par l'extension donc nous ne faisons que remplir ou surcharger des blocs.
La seule astuce est l'inclusion d'une feuille de style complémentaire :
{% block styles %}
   {{super()}}
   <link rel="stylesheet" href="{{url_for('static', filename ='css/Base.css', _external = True)}}" />
{% endblock %}


       
 
0n redéfini le block style, on place la commande {{super()}} pour remettre le code de base.html puis on ajoute notre feuille de style. C'est la même technique pour le bloc body

2) Changer la page Hello (hello3.html):

{% extends "MyBase2.html" %}
{% block title %} Hello avec Boot Stap{% endblock %}
{% block content %}
  {% if name %}
     <h1>Hello {{ name }}!</h1>
  {% else %}
     <h1>Hello World!</h1>
  {% endif %}
{% endblock %}     
Rien de fondamental, changer bien la page de base en Mybase2.html

3) changer le test.py :

On ajoute l'import de l'extension :
from flask.ext.script import Manager
On déclare le plug-in :
#déclare le plug-in flask-bootStrap
bootstrap = Bootstrap(app)
 
Et on ajoute une route pour hello3 à partir de la ligne 36.
Voici le résultat ou le H1 de bootstrap est appliqué :

G) La barre de navigation :

Nous allons créer une barre de navigation en haut du site reprenant l'ensemble des routes du site.
Encore une fois nous pourrions faire cette barre à la main, mais sa création peut être facilité par l'emploie du plug-in Flask-Nav.
Les fichiers pour ce tuto sont : 6test.py, mybase3.html et hello4.html
Commençons par créer la barre de navigation dans le code du serveur d'application (6test.py), voici les lignes commentés que j'ai ajoutées.
from flask_nav.elements import Navbar, View

#déclare le plug-in flask-script
manager = Manager( app)

#déclare le plug-in flask-bootStrap
bootstrap = Bootstrap(app)

#j'instancie le plug-in flask-Nav
nav = Nav()

#je déclare le plug-in dans l'application
nav.init_app(app)

#je déclare une barre de navigation contenant les routes
mynavbar = Navbar(
      'mysite',
      View('Home', 'index'),
      View('1er Exemple', 'hello'),
      View('template Exemple', 'hello2'),
      View('BootStrap Exemple', 'hello3'),
      View('BootStrap et Nav Exemple', 'hello4'),
)

#je donne au plug-in ma barre de navigation
nav.register_element('top', mynavbar)
Flask-Nav fait appel à la notion de view, celle-ci sont les fonctions qui sont attachées au route (ex: hello)
Au-delà de déclarer le plug-in le point important est de créer une barre de navigation avec la fonction NavBar, qui prend en paramètre le nom de la barre et des déclarations view avec leur nom et la fonction correspondante.
Puis on déclare la barre dans le plug-in :
nav.register_element('top', mynavbar)      
Voilà pour le fichier serveur, passons à la page Mybase3.html qui permet le rendu de la barre. J'ai juste ajouté dans le « block navbar » la commande nav.top.render() où top correspond au nom de la barre donnée lors de l'enregistrement dans le plug-in:
{% block navbar %}
{{nav.top.render()}}
{% endblock %}
Voici le résultat :

Voila nous arrivons à la fin de ce 1er tuto sur flask, j'espère que vous aurez apprècié la simplicité de ce serveur d'application.

Aucun commentaire:

Enregistrer un commentaire