TD n°2 : Déploiement§

Dans ce TD, nous allons voir comment publier notre application Flask sur un service d’hébergement gratuit.

Nous utiliserons https://www.pythonanywhere.com/.

Création d’un compte§

Chargement des fichiers§

  • Compressez les fichiers de votre application dans un fichier ZIP
  • Depuis l’onglet Files de PythonAnywhere, créez un répertoire tp-ensembl, et téléversez-y votre fichier ZIP
  • Cliquez ensuite sur Open Bash console here
  • Dans la console, décompressez votre archive avec la commande unzip VOTRE_FICHIER.zip
  • Téléversez ensuite le fichier Sqlite dans le même répertoire (cela va prendre un certain temps)

Création de l’application§

  • Depuis l’onlget Web, cliquez sur Add a new web app
  • Sélectionnez Flask et Python 3.6
  • Choisissez comme fichier un fichier inexistant dans le répertoire tp-ensembl, par exemple /home/LOGIN/tp-ensembl/flask_app.py
  • Ouvrez l’application : elle affiche “Hello from Flask”
  • Depuis une console Bash, ouverte dans tp-ensembl, supprimez flask_app.py et remplacez le par un lien symbolique (ln -s) vers votre propre fichier Python (ou renommez votre fichier Pyhon en flask_app.py)
  • Depuis l’onglet Web, rechargez l’application
  • Ouvrez l’application : c’est la vôtre !
  • Vérifiez que votre application fonctionne correctement
  • Testez l’application de votre voisin⋅e,

Pour aller plus loin : MySQL§

La base de données Sqlite3 que nous utilisons est parfaite pour développer, mais pas pour un site en production.

  • Dans l’onglet Files, téléversez ce fichier à la racine de votre compte (cela peut prendre un certain temps)
  • Ouvrez l’onglet Databases, sous-onglet MysSQL
  • Choisissez un mot de passe pour la base de données (différent du mot de passe de votre compte)
  • Dans Create database, entrez le texte ensembl et cliquez sur Create
  • Ouvrez une console en cliquant sur le lient LOGIN$ensembl
  • Importez le contenu de la base Ensembl à l’aide de la commande source ensembl_hs63_simple.sql
  • L’import prend un certain temps, ignorez les erreurs
  • Vérifiez que tout s’est bien passé avec la requête select * from Genes limit 10;

Nous pouvons maintenant modifier le fichier Python pour qu’il utilise la base de données MySQL ; il suffit normalement d’importer mysql.connector, et de remplacer la chaîne de connexion à la base par :

cx =  mysql.connector.connect(
    user="LOGIN",
    password="MYSQL-PASSWD",
    host="LOGIN.mysql.pythonanywhere-services.com",
    database="LOGIN$ensembl"
)

Bien sûr, il n’est pas très judicieux de laisser ces informations dans le code Python. On préfère en général les stocker dans un fichier séparé, et lire ce fichier depuis le code python.

On peut même, dans ce fichier de configuration, autoriser deux types de configuration, Sqlite3 et MySQL, et améliorer le code Python pour qu’il utilise l’un ou l’autre système en fonction de la configuration. Ainsi, ce fichier sera le seul à différer entre votre environnement de développement (sur votre machine) et l’environnement de production (chez l’hébergeur).

Avertissement

Compatibilité Sqlite3 / MySQL

Les bibliothèques Sqlite3 et MySQL en Python sont globalement compatibles, car elles respectent le même standard DbAPI2.

Cela étant dit, chacune propose ses propres extensions du standard, donc selon la manière de coder, votre programme peut nécessiter des adaptations pour fonctionner avec MySQL.

Notamment, les fonctionalités suivantes de Sqlite3 ne sont pas standard :

  • exécution de requête directe depuis l’objet connexion :

    # n'écrivez pas
    c = cx.execute(query)
    # mais écrivez
    c = cx.cursor()
    c.execute(query)
    
  • résultat sous forme de dicionnaire :

    cx.row_factory = sqlite3.Row # pas supporté par MySQL
    

    Pour reproduire cette fonctionalité de manière standard, on peut par exemple passer par la fonction suivante :

    def fetchall_dict(c):
        """ Utilisez     fetchall_dict(c)
            au lieu de   c.fetchall()
            pour récupérer des dictionnaires au lieu de tuples.
        """
        col_names = [ d[0] for d in c.description ]
        for row in c.fetchall():
            yield dict(zip(col_names, row))