• 8 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 01/02/2019

Découpez votre projet en templates

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

 

Flask utilise le moteur de templates Jinja 2 par défaut. Ce dernier nous permet d'utiliser certaines méthodes dans les templates pour nous simplifier la vie : lien vers d'autres pages, import d'autres templates... Nous allons voir tout cela en détail dans ce chapitre.

Utiliser une méthode dans le template

Nous avons réussi à afficher le template index.html ainsi que les feuilles de style. Mais nous pourrions faire mieux ! Le template n'est pas qu'un fichier HTML classique car vous avez accès à de nombreux objets qui vous faciliteront la vie.

Flask offre, par exemple, une méthode plutôt intéressante pour faire appel à des fichiers qui se trouvent dans le dossier static : url_for(). Cette méthode est disponible dans le template. 

Que fait-elle ? Elle génère une URL en fonction des paramètres transmis.

Le premier paramètre est le nom du dossier qui contient les fichiers que l'on souhaite lier. Le second, optionnel, est le nom d'un fichier. Par exemple :

url_for('static', filename='css/bootstrap/bootstrap.min.css')

Ceci générera le lien suivant :  /static/css/bootstrap/bootstrap.min.css.

Intégrez-la dans index.html ! Mais si vous vous contentez d'insérer la méthode telle quelle dans le template, elle sera interprétée comme du HTML pur.

Afin d'indiquer à Jinja 2 que vous insérez du Python, qui doit donc être interprété comme tel, entourez le code d'accolades. Comme ceci :

<div>
{{ url_for('static', filename='css/bootstrap/bootstrap.min.css') }} <!-- La phrase sera affichée dans la page -->
{% if blur: %}Je ne m'affiche que si "blur" est vrai.{% endif %}
{# ceci est un commentaire #}
</div>

Remplacez tous les liens de la page ! Oui je sais, il y en a beaucoup... :'(

Objets disponibles dans le template

La méthode url_for() fait partie des objets disponibles dans le template. Voici les autres :

  • config : dictionnaire comprenant la configuration de l'application (dans notre cas, les éléments qui se trouvent dans config.py).

  • request : les détails de la requête reçue.

  • session : la session actuelle. Vous pouvez en effet stocker des valeurs dans la session pour la retrouver ultérieurement. Il s'agit d'une pratique très courante pour authentifier un utilisateur et se souvenir de son passage.

  • g : variables globales. 

  • get_flashed_messages() : messages flash. Nous n'entrerons pas dans le détail de cette méthode mais je vous invite à lire la documentation pour en savoir plus.

Je vous propose d'utiliser la variable config pour afficher le contenu de la variable FB_APP_ID de config.py :

window.fbAsyncInit = function() {
  FB.init({
    appId      : '{{ config['FB_APP_ID'] }}',
    cookie     : true,  // enable cookies to allow the server to access
                        // the session
    xfbml      : true,  // parse social plugins on this page
    version    : 'v2.8' // use graph api version 2.8
  });
}  

  ...

  (function(d, s, id) {
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s); js.id = id;
  js.src = "https://connect.facebook.net/fr_FR/sdk.js#xfbml=1&amp;version=v2.9&amp;appId={{ config['FB_APP_ID'] }}";
  fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));

Si vous rafraîchissez la page, vous voyez maintenant apparaître le bouton 'Continuer avec Facebook' en bas de la page. Bravo ! 

Héritage

Passons maintenant à la page de résultats result.html. Définissez une nouvelle vue :

@app.route('/result/')
def result():
    return render_template('result.html')

En ouvrant   result.html, vous remarquez aisément qu'une partie du code se répète. Par exemple, les appels à l'API de Facebook. Vous avez mis à jour l'identifiant Facebook de l'index.html mais vous devriez également en faire de même ici ! 

Mais que nenni, mon bon ami ! Jinja 2 nous permet de créer un template que l'on pourra étendre à volonté.

Étendre ? Tu veux dire, comme du linge ?

Je ne réagirai pas à cet essai de blague. Non, étendre comme une classe enfant qui hériterait d'une classe parent : vous pouvez changer les valeurs par défaut du template parent. C'est assez simple !

Créez un template base.html qui contiendra le squelette de toutes nos pages : headfooter...

index.html contiendra alors ce qui sera amené à changer dans le template d'origine : le contenu du body. 

Indiquez à présent que vous souhaitez étendre base.html dans le template result.html. Vous pouvez faire cela en utilisant le mot-clé extends 'nomdutemplate.html' :

result.html

{% extends 'base.html' %}

...

Si vous rechargez la page, vous verrez que le contenu du template base.html s'affiche... mais pas celui de result.html. Pourtant il y a bien du contenu dans ce dernier !

Vous vous en doutez, c'est normal... Le template parent agit un peu comme un texte à trou : vous devez indiquer les endroits qui seront amenés à changer.

Utilisez le mot-clé block nomdemonblock comme ceci :

base.html

  ...
</nav>

{% block content %}{% endblock %}

<footer class="text-center">
  ...

Jinja 2 sait maintenant que du contenu peut être inséré à cet endroit.

Dans le template enfant, result.html, englobez le contenu à envoyer dans le même bloc :

result.html

{% block content %}
  <!-- Header -->
  <header>
    ...
  </script>
{% endblock %}

Je vous laisse en faire de même pour le template index.html ! 

Passer des variables au template

Notre page de résultats est bien jolie mais il est grand temps de la rendre dynamique ! Autrement dit, de pouvoir changer les éléments suivants en fonction de l'utilisateur :

  • prénom

  • photo de profil

  • description

La méthode render_template(), que nous utilisons dans la vue, est un peu spéciale. Vous pouvez lui donner en paramètres toutes les variables que vous souhaitez utiliser dans le template. Par exemple, le prénom :

views.py

@app.route('/result/')
def result():
    return render_template('result.html',
                           user_name="Tom")

result.html

...
<h2 id="user_name">{{ user_name }}</h2>
...

Continuez ainsi en remplaçant la photo de profil et la description :

views.py

@app.route('/result/')
def result():
  description = """
      Toi, tu n'as pas peur d'être seul ! Les grands espaces et les aventures sont faits pour toi. D'ailleurs, Koh Lanta est ton émission préférée ! Bientôt tu partiras les cheveux au vent sur ton radeau. Tu es aussi un idéaliste chevronné. Quelle chance !
  """
  return render_template('result.html',
                          user_name='Tom',
                          user_image=url_for('static', filename='tmp/cover_111823112767411.jpg'),
                          description=description)

result.html

...
<div class="col-sm-3 col-sm-offset-3">
  <img class="img-responsive img-circle user_profile" src="{{ user_image }}" id="user_image">
</div>
<div class="col-sm-3">
  <h2 id="user_name">{{ user_name }}</h2>
</div>
<div class="col-sm-12">
  <p id="description">{{ description }}</p>
</div>
...

Faites-en de même pour la vue index

Import de template

Vous touchez presque au but !

Avant de passer au prochain chapitre, refactorisez un peu les templates. Vous ne trouvez pas qu'une partie du template se répète ?

Cette partie est exactement la même entre index.html et result.html désormais :

<div class="card">
  <div class="row">
    <div class="col-sm-3 col-sm-offset-3">
        <img class="img-responsive img-circle user_profile" src="{{ user_image }}" id="user_image">
    </div>
    <div class="col-sm-3">
      <h2 id="user_name">{{ user_name }}</h2>
    </div>
    <div class="col-sm-12">
      <p id="description">{{ description }}</p>
    </div>
  </div>
</div>

La seule différence est la classe blur, présente dans l'index et non dans la page de résultats.

Je vous propose de passer la valeur de blur dans la vue et de créer une structure conditionnelle dans le template :

views.py

@app.route('/')
@app.route('/index/')
def index():
    ...
    return render_template('index.html',
                            user_name='Julio',
                            user_image=url_for('static', filename='img/profile.png'),
                            description=description,
                            blur=True)

result.html et index.html

<p id="description" {% if blur: %}class="blur"{% endif %}>{{ description }}</p>

Créez un nouveau template, card.html, dans lequel vous collerez la portion de code qui se répète. Puis incluez-le dans result.html et index.html en utilisant la méthode include.

index.html et result.html

<div class="col-lg-6 col-lg-offset-3">
  {% include 'card.html' %}
</div>

Rechargez les pages et admirez le travail !

Code du chapitre

Retrouvez le code de ce chapitre à cette adresse.

Exemple de certificat de réussite
Exemple de certificat de réussite