Mots-clé : template

Django : comment vérifier si on est en mode debug dans le template

Vous trouverez l’information dans beaucoup de sites, et cela semble très simple : dans votre template, il suffit de faire :

{% if not debug %}Je suis en debug !{% endif %}

En réalité cela ne suffit pas.

Il faut dans votre fichier settings.py, configurer correctement les adresses IP’s qui précisent qu’on est / ou pas / en mode « développement » :

INTERNAL_IPS = ['127.0.0.1', ]

(Notez que ce code peut être largement optimisé et dépendant de l’environnement, par exemple j’ai fait un settings.py qui prend cela en compte)

Django : faire des pages d’erreur sur mesure (404, 500, etc.)

Lorsque vous voulez faire des pages d’erreur sur mesure, c’est très simple… une fois que vous savez !
En effet, la documentation n’est pas très claire…

Voici un résumé de mon expérience, pour faire des pages d’erreur sur mesure (404, 500, etc.).
Il faut d’abord savoir que les erreurs sont des fonctions appelées (= vous ne pouvez pas le faire via les vues génériques).
Ensuite, la documentation donne un exemple, mais ce n’est pas assez.
Grâce aux raccourcis de Django, vous avez la fonction render() à laquelle vous pouvez passer un template et un contexte (= donc des variables).
Je me suis servi de cela pour créer un dictionnaire qui contient les erreurs, et les passer dans dans le contexte avec la clé title et content.


Voici le code des vues qui affiche une erreur « proprement » :

from django.shortcuts import render
from django.utils.translation import gettext_lazy as _
VIEW_ERRORS = {
    404: {'title': _("404 - Page not found"),
          'content': _("A 404 Not found error indicates..."), },
    500: {'title': _("Internal error"),
          'content': _("A 500 Internal error means..."), },
    403: {'title': _("Permission denied"),
          'content': _("A 403 Forbidden error means ..."), },
    400: {'title': _("Bad request"),
          'content': _("A 400 Bad request error means ..."), }, }
def error_view_handler(request, exception, status):
    return render(request, template_name='errors.html', status=status,
                  context={'error': exception, 'status': status,
                           'title': VIEW_ERRORS[status]['title'],
                           'content': VIEW_ERRORS[status]['content']})
def error_404_view_handler(request, exception=None):
    return error_view_handler(request, exception, 404)
def error_500_view_handler(request, exception=None):
    return error_view_handler(request, exception, 500)
def error_403_view_handler(request, exception=None):
    return error_view_handler(request, exception, 403)
def error_400_view_handler(request, exception=None):
    return error_view_handler(request, exception, 400)


Une fois les vues faites, il faut aller dans la déclaration principale de vos vues. Donc le fichier urls.py qui est la racine de votre projet. Si vous mettez le code ailleurs, il sera ignoré.

Dedans, déclarez vos fonctions qui gèrent les erreurs :

handler404 = 'app.views.errors.error_404_view_handler'
handler500 = 'app.views.errors.error_500_view_handler'
handler403 = 'app.views.errors.error_403_view_handler'
handler400 = 'app.views.errors.error_400_view_handler'

Et enfin, dans vos templates, créez un fichier errors.html dans lequel vous pouvez construire votre page HTML qui gère l’erreur « proprement », avec, en plus dans le contexte, les variables {{ title }} et {{ content }} qui affichent respectivement le titre et le détail de l’erreur.

Maintenant, comment, en mode « développement », tester ces pages ? Dans la pratique vous ne pouvez pas, car en mode développement, une erreur affiche les informations de déboguage, et pas vos pages !
La solution : faire une URL qui « simule » l’erreur. Exemple avec 404 : vous ajouter dans vos urls : path('404/', error_404_view_handler), et puis d’afficher votre URL adéquate, soit http://localhost:8000/404/ et vous verrez l’erreur !

Django : comment passer une constante à tous vos templates ?

Django, nativement, a la possibilité de passer des dictionnaires clé-valeurs à tous les templates, et dans les templates on accède aux valeurs via la clé.

Exemple concret : je voulais passer le nom de mon site à tous les templates, mais sous forme de constante.

Dans settings.py j’ai défini ma constante : WEBSITE_NAME = 'mywebsite'

Il suffit de créer une fonction qui renvoie un dictionnaire avec une clé nommée « correctement », par exemple :

def context_processor_website_name(request):
    return {'website_name': WEBSITE_NAME}

Et ensuite, dans les context_processors, juste ajouter le nom de la fonction par rapport au package où elle est.
J’ai mis cette fonction dans settings.py.
Je ne sais pas si c’est la meilleure place mais l’idée c’est que comme c’est en rapport avec la configuration de mon site, c’est le meilleur endroit…

Donc pour préciser où est la fonction c’est 'myproject.settings.context_processor_website_name' :

On retrouve donc le code final comme ceci :

TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [os.path.join(BASE_DIR, 'templates')],
    'APP_DIRS': True,
    'OPTIONS': {
        'context_processors': [
            'django.template.context_processors.debug',
            # ... and:
            'myproject.settings.context_processor_website_name'
    ],
    },
}, ]

Et voilà, à partir de maintenant, avec exactement 3 lignes de code, j’ai accès à ma variable website_name, que je peux utiliser ainsi :

<title>{% block title %}{% if title %}{{ title|safe }} - {% endif %}{{ website_name }}{% endblock %}</title>

Petite parenthèse : grâce à ce code, si la page qu’on affiche a un titre, elle aura pour titre : [titre] - [nom du site] et si elle n’a aucun titre, elle affichera : [nom du site]

symfony 2 : isset et null avec Twig

Une des questions qu’on cherche le plus souvent avec Twig, c’est comment vérifier si une variable existe ou pas ?

La réponse se fait en trois étapes :

  1. Vérifier si une variable existe (= si elle a été initialisée) :
    En php, c’est isset().
    En Twig, c’est defined.
    Exemple de code Twig :

    {% if app.user is defined %}
    Code html
    {% endif %}
  2. Vérifier si une variable n’est pas null (= elle est définie, mais contient la valeur null) :
    En php, c’est !is_null().
    En Twig, c’est is not null.
    Exemple de code Twig :

    {% if app.user is not null%}
    Code html
    {% endif %}
  3. Vérifier si une variable existe et qu’elle n’est pas null (= elle est définie, et contient autre chose que la valeur null) :
    En php, c’est isset() && (!is_null()).
    En Twig, c’est is defined and is not null.
    Exemple de code Twig :

    {% if app.user is defined and app.user is not null%}
    Code html
    {% endif %}

En espérant que cela aide du monde !

Symfony 2: générer une url dynamique dans twig dans du code javascript

J’ai eu à faire face à un problème que vous allez très certainement rencontrer si vous faites Symfony2. Dans les fichiers de template, vous allez sûrement mettre du Javascript. Exemple :

<script type="text/javascript">
<!--
function verif_formulaire(){
    window.location = '/test/mon/url/';
}
-->
</script>

Maintenant, si on essaie de faire ça en Twig, c’est simple. Je ne m’attarderai que sur l’URL :

    window.location = '{{ path('my_path') }}';

Supposons que votre path nécessite un paramètre, par exemple, dans mon cas, le code postal :

    window.location = '{{ path('my_path', {'cp': "13480" }) }}';

Facile. Mais supposons que votre code en JavaScript veuille le générer dynamiquement :

/* récupération de la valeur quelque part : */
var monCP = document.getElementById('cp').value;
window.location = ="{{ path('hqf_pizzas_searchpage', {'cp': monCP }) }}";

Eh bien ça ne fonctionnera pas sur Symfony2. Ne cherchez pas c’est comme ça. Vous aurez une erreur. L’erreur, pour reprendre mon code, était :

Variable "monCP" does not exist in HQFPizzasBundle:Default:index.html.twig at line 11

Voici la solution que j’ai trouvée : je reprends ma configuration et il vous suffira de l’adapter à vos besoins. Dans mon fichier de routing src/HQF/Bundle/PizzasBundle/Resources/config/routing.yml, j’ai ce path qui nécessite le paramètre cp :

hqf_pizzas_searchpage:
    pattern:  /search/cp/{cp}
    defaults: { _controller: ... }

L’objectif est de ressortir le path avec un '%s' dedans, de manière à pouvoir avoir une URL qui ressemble à :

/search/cp/%s

Ainsi, il suffira juste après d’utiliser la fonction Twig ‘format‘ et d’y passer la variable JavaScript, par exemple monCP.
Ainsi cet ordre twig:

{{ path('hqf_pizzas_searchpage', {'cp': "%s" }) | format('monCP') }}

génèrera ceci :

/search/cp/monCP

L’objectif final est de sortir du vrai code JavaScript qui ressemble à :

window.location="/search/cp/"+monCP+"";

Donc si veut y arriver, le mélange code Twig + JavaScript, avec les guillemets, devrait être :

window.location = "{{ path('hqf_pizzas_searchpage', {'cp': "%s" })  | format('"+monCP+"') }}"

Seulement, problème : il escape tout ! Le code généré sera ainsi :

window.location ="/pizzas/search/cp/%25s";

Solution : pour que twig n’échappe pas le texte, il faut créer son propre filtre Twig !

Voici les étapes à suivre :

J’ai crée mon fichier src/HQF/Bundle/PizzasBundle/Twig/UrlDecodeExtension.php dans lequel j’ai mis ce code :

<?php

namespace HQF\Bundle\PizzasBundle\Twig;

class UrlDecodeExtension extends \Twig_Extension
{
    public function getFilters()
    {
        return array(
            'url_decode' => new \Twig_Filter_Method($this, 'urlDecode'),
        );
    }

    public function urlDecode( $url )
    {
        return urldecode( $url );
    }

    public function getName()
    {
        return 'url_decode_extension';
    }
}

Ensuite, je l’ai enregistré dans les services.
C’est dans le fichier src/HQF/Bundle/PizzasBundle/Resources/config/services.yml les lignes :

services:
    cme.twig.url_decode_extension:
        class: HQF\Bundle\PizzasBundle\Twig\UrlDecodeExtension
        tags:
            - { name: twig.extension }

A partir de là le filtre url_decode fonctionne. Il suffit de faire le code qui suit :

/* récupération de la valeur quelque part */
var monCP = document.getElementById('cp').value;
window.location ="{{ path('hqf_pizzas_searchpage', {'cp': "%s" }) | url_decode | format('"+monCP+"') | raw }}";

Afin de générer cela en JavaScript :

/* récupération de la valeur quelque part */
var monCP = document.getElementById('cp').value;
window.location ="/pizzas/search/cp/"+monCP+"";

Ce qui est du code JavaScript parfaitement exécutable.

Voici les explications pas à pas :

window.location ="{{ path('hqf_pizzas_searchpage', {'cp': "%s" }) }}";

Génère cela :

window.location ="/pizzas/search/cp/%25s";

C’est échappé, et il ne le faut pas ! Donc ajouter le filtre url_decode :

window.location ="{{ path('hqf_pizzas_searchpage', {'cp': "%s" }) | url_decode }}";

Là le résultat sera celui attendu :

window.location ="/pizzas/search/cp/%s";

Ensuite on y ajoute la fonction format afin d’y ajouter la variable JavaScript :

window.location ="{{ path('hqf_pizzas_searchpage', {'cp': "%s" }) | url_decode | format('"+monCP+"') }}";

Mais là encore le résultat sera échappé :

window.location ="/pizzas/search/cp/&quot;+monCP+&quot;";

Donc il faut lui dire de ressortir le résultat final au format raw :

window.location ="{{ path('hqf_pizzas_searchpage', {'cp': "%s" }) | url_decode | format('"+monCP+"') | raw }}";

Et le résultat de sortie sera (enfin !) parfait :

window.location ="/pizzas/search/cp/"+monCP+"";

Cet article est un mélange de l’explication de création des filtres Twig, ici, et de la question qui ressemble très fortement à la mienne sur stackoverflow, ici.

En espérant que cela serve à quelqu’un, un jour 😉

Smarty : dump de variable : écrire une fonction plugin

Vous avez sûrement déjà été confronté au fait de vouloir afficher le contenu d’une variable Smarty.
J’ai décrit la version courte ici.

Maintenant, il peut arriver que l’affichage ne corresponde pas à ce que vous vouliez notamment parce que le print_r() et autre var_dump() affichent des retour chariot.

J’ai donc crée ma fonction plugin, qui fait cela :


  /**
   * Affiche le contenu d'une variable.
   * Utilisation : {dump var=$variable_smarty}
   *
   * @param array $params Tableau de parametres
   * @param object $smarty objet Smarty
   * @return string Le dump
   */
  public function smarty_dump($params,$smarty)
  {
    // Récupération des paramètres
    if (!isset($params['var'])) {
      throw new Exception(
        "dump : paramètre 'var' obligatoire");
    }
    return
      '<pre>'.
      str_replace(
        "\n", "<br />",
        str_replace(
          "\r", "",
          var_export($params['var'],true)
        )
      ).
      '</pre>';
  }

Ensuite, je déclare le plugin à Smarty :

$smarty->registerPlugin('function','dump',
  array($this,'smarty_dump'));

Et dans mon template, je l’appelle ainsi :


<table>
{foreach from=$tab_devis item=a}
  <tr>
    <td>
      {dump var=$a}
    </td>
  </tr>
{/foreach}
</table>

Et l’affichage devient parfait : il est à l’intérieur d’une balise <pre></pre>, avec retour à la ligne <br /> comme il faut.

En espérant que cela serve à des utilisateurs