Jinja2

Da Wikipedia, l'enciclopedia libera.

Jinja2 è un motore di template attrezzato per il linguaggio Python e per questo il più utilizzato da Python[1]. Viene utilizzato da framework per applicazioni web come Flask. Creato da Armin Ronacher, creatore anche di Flask, e rilasciato il 9 giugno 2008 con la versione 2.0rc1, è continuamente soggetto a aggiornamenti ed è disponibile sotto la licenza BSD. Il progetto è indipendente e open source, supporta completamente Unicode ed è provvisto di una sandbox, cioè un ambiente di esecuzione di prova integrato opzionale.

Jinja2
SviluppatoreArmin Ronacher
Data prima versione9 giugno 2008
Ultima versione2.9.6 (3 Aprile 2017)
Sistema operativoMultipiattaforma
Linguaggiopython
Python (non in lista)
GenereMotore di template (non in lista)
LicenzaBSD
(licenza libera)
Sito web

Caratteristiche principali[modifica | modifica wikitesto]

Jinja2 permette la creazione di file HTML, XML o in altri formati di markup, che vengono restituiti all'utente mediante una risposta HTTP. Le versioni di Python su cui può essere utilizzato sono la 2.4 e le successive. Jinja2 non è l'unico motore di template esistente, né il primo a essere stato creato. La sua sintassi è, al contrario, ispirata a Django, la cui nascita risale al 2003. Diversamente da Django, tuttavia, Jinja2 ha un linguaggio espressivo e fornisce un set di strumenti più potente. Tra le innovazioni ci sono la sandbox e il sistema di escape automatico. Inoltre, è interamente basato su Unicode.

Jinja2 viene utilizzato da framework per applicazioni web come Flask, Bottle, Morepath e Django, e da tools come Ansible, Pelican e simili. Presenta una sintassi consistente[2] e, in aggiunta rispetto agli altri motori di template, permette da un lato di inserire il codice all'interno dei template, dall'altro di essere sviluppato.

  • Modalità di esecuzione nella sandbox.
  • Sistema di escape HTML automatico, per prevenire attacchi cross-site scripting (XSS).
  • Ereditarietà dei template: è possibile usare uno stesso layout o uno simile per tutti i template.
  • Alte prestazioni per la compilazione in tempo reale del codice Python.
  • Facilità nel debugging.
  • Sintassi configurabile. Per esempio, può essere riconfigurato per adeguarlo a output nei formati LaTeX o Javascript.
  • Vasta gamma di piccoli helpers utili che aiutano a risolvere compiti comuni nei template, come la divisione di sequenze di oggetti in più colonne e altro.

Nozioni di base[modifica | modifica wikitesto]

Template[modifica | modifica wikitesto]

In Jinja2, un template è un file che contiene il testo di una risposta. Al suo interno possono esserci variabili e/o espressioni per le parti dinamiche, che possono essere rimpiazzate con valori al momento della resa del template, e tag che controllano la logica del template. Il file non necessita di una estensione specifica, può essere in qualsiasi formato (HTML, XML, CSV, LaTeX, etc). Jinja2 utilizza le cosiddette classi loader per caricare i template. Una volta inizializzata la classe loader, è possibile creare un'istanza della classe jinja2.Environment. Questa classe è basilare ed è usata per immagazzinare le variabili di configurazione, accedere ai template e passare le variabili agli oggetti del template.

Il modo più semplice per configurare Jinja2 e caricare template per l'applicazione è questo:

   from jinja2 import Environment, PackageLoader, select_autoescape
   env = Environment(
         loader=PackageLoader('yourapplication', 'template'),
         autoescape=select_autoescape(['html', 'xml'])
   )[3]

Una volta creato l'Environment, è possibile caricare i template e restituire l'output mediante due metodi: get_template e render. Il risultato ottenuto può essere scritto in un file. Si devono passare tutte le variabili al template, come in un dizionario: le chiavi sono i nomi delle variabili disponibili all'interno del template; i valori sono oggetti Python che si vogliono passare al template.

Il processo che rimpiazza le variabili con i valori effettivi e restituisce la stringa di risposta, è chiamato rendering. La componente dinamica del template di Jinja2 è rappresentata da una variabile racchiusa tra parentesi graffe doppie, in questo modo: {{ variabile }}. Per caricare un template da un Environment è sufficiente chiamare il metodo get_template() che restituisce il template caricato, mentre per restituirlo con alcune variabili, occorre chiamare il metodo render().

Il Context del template contiene le variabili di un template, cioè immagazzina i valori passati al template e anche i nomi che il template esporta. Il Context è immutabile per la stessa ragione per cui i frame locali di Python sono immutabili all'interno delle funzioni. Inoltre, Jinja2 non usa il Context come contenitore di dati per le variabili, ma solo come risorse di dati primari, esattamente come Python per i frame locali.

Sandbox[modifica | modifica wikitesto]

La Sandbox di Jinja2 può essere utilizzata per valutare codici non sicuri, se il template prova a accedere a un codice non sicuro interviene un SecurityError. Tuttavia, ci sono anche alcune eccezioni che la sandbox non è in grado di controllare e che è compito del creatore del codice assicurarsi siano catturate.

Auto-escaping[modifica | modifica wikitesto]

L'auto-escaping è una configurazione di Jinja2 per la quale, se non esplicitamente richiesto il contrario, qualsiasi cosa venga stampata in un template viene interpretata come testo libero. Immaginiamo che il valore di una variabile x sia <b>ciao</b>. Se l'auto-escaping è abilitato, {{ x }} in un template produrrà la stringa esattamente come è data, cioè <b>ciao</b>. Se invece è disabilitato, che è il default di Jinja2, il testo risultante sarà ciao.

Quando si genera HTML dai template, le variabili possono includere caratteri che influenzano l'HTML risultante. I due approcci possibili sono:

  1. L'escaping manuale di ogni variabile
  2. L'escaping automatico

Se è abilitato l'escaping manuale, è responsabilità del creatore del codice controllare le variabili, utilizzando se necessario il filtro |e per determinare quali siano quelle variabili su cui l'escaping deve agire:

{{ variabile|e }}

In questo modo, il valore della variabile sarà visualizzato come normale testo anche se conterrà caratteri speciali che altrimenti modificherebbero l'output, cioè < , > , & oppure " .

Quando invece è abilitato l'escaping automatico, questo viene applicato su tutto eccetto per i valori marcati esplicitamente come sicuri. Le variabili e le espressioni possono essere marcate come sicure nel dizionario del Context con MarkupSafe.Markup o nel template con il filtro |safe.

Ereditarietà di template[modifica | modifica wikitesto]

La parte più potente di Jinja è l'ereditarietà del template che permette di costruire uno scheletro di base del template contenente tutti gli elementi comuni ai vari template e di definire blocchi che i template figli possono ereditare. Il template genitore definisce quindi un modello attraverso la costruzione di blocchi taggati con block che avvertono il motore di template della loro possibilità di essere ereditati dai template figli. Ogni template figlio può quindi riempire tali blocchi vuoti con contenuto usando il tag {% extends %} per comunicare al motore di template che il template su cui si trova il tag estende un altro template, cioè quello genitore. Jinja2 una volta incontrato extends individua il template genitore corrispondente. I blocchi possono essere annidati per layouts più complessi, ma non ci possono essere due blocchi, all'interno dello stesso template, con lo stesso nome.

API[modifica | modifica wikitesto]

Jinja2 è stato creato per essere utilizzato da framework per applicazioni web e, per questo motivo, ha una API molto estensiva. Possiamo descrivere tre differenti tipologie di API:

  1. L'API di alto livello è l'API che si usa nell'applicazione per caricare e restituire i template.
  2. L'API di basso livello è utile solo se si vuole scavare più a fondo in Jinja2 o sviluppare estensioni, offre funzionalità utili per comprendere alcuni dettagli dell'implementazione, per il debugging e per tecniche di estensione avanzate.
  3. L'API Meta restituisce alcune informazioni sugli alberi di sintassi astratti che possono aiutare le applicazioni a implementare concetti di template più avanzati. Tutte le funzioni dell'API Meta operano su un albero di sintassi astratto.

Delimitatori[modifica | modifica wikitesto]

Ci sono diversi tipi di delimitatori nel template, quelli di default sono:

  • {% ... %} per le istruzioni
  • {{ ... }} per le espressioni e le variabili
  • {# ... #} per i commenti, non inclusi nell'output del template
  • # ... ## per istruzioni in linea

Quando c'è necessità di restituire come output caratteri che normalmente verrebbero trattati come delimitatori, è sufficiente racchiuderli tra apici e poi inserirli all'interno dei delimitatori utilizzati per le espressioni e le variabili, cioè le {{ }}:

  {{ ‘{{‘ }} che restituisce {{

Per sezioni più grandi, ha senso racchiudere tutto in un blocco raw:

   {% raw %}
      …
   {% endraw %}

Variabili[modifica | modifica wikitesto]

Le variabili del template sono definite nel dizionario del Context passato al template e riconosciute attraverso i nomi assegnati come chiavi nel dizionario. Il costrutto {{ … }} usato nei template si riferisce a una variabile. Questo segnaposto speciale comunica al motore di template che il valore da sostituire in quella posizione deve essere ottenuto dai dati forniti nel momento in cui il template viene reso. Jinja2 riconosce le variabili di qualsiasi tipo, anche i tipi complessi come liste, dizionari e oggetti.

Le variabili possono avere attributi o elementi ai quali si può accedere. Si può usare un punto (.) per accedere agli attributi oppure, come nello standard Python, le parentesi quadre []:

  {{ foo.bar }}
  {{ foo[bar] }}

Se una variabile o un attributo non esistono, il risultato è un valore indefinito. Questo valore, per default, restituisce una stringa vuota per quanto riguarda operazioni di stampa e iterazione, mentre fallisce per qualsiasi altra operazione.

In Jinja2 gli i nomi assegnati come chiavi validi devono concordare con l'espressione regolare [a-Za-z_] [a-z-A-Z0-9]*, cioè quella valida per Python 2.x, e i caratteri non ASCII non sono ammessi.

Filtri[modifica | modifica wikitesto]

Le variabili possono essere modificate dai filtri, separati da esse dal simbolo pipe ( | ), che possono avere opzionalmente argomenti tra parentesi. I filtri multipli possono essere concatenati e in questo caso l'output di un filtro è applicato al successivo. Gli argomenti dei filtri vengono sono racchiusi tra parentesi. Per utilizzare un filtro è sufficiente utilizzare questa struttura

   {{ variabile | filtro }}

L'espressione regolare per gli identificatori di filtri è [a-Za-z_] [a-z-A-Z0-9]*(\.[a-zA-Z_][a-zA-Z0-9_]*)* .Jinja2 ha una quantità enorme di filtri disponibili per default; tra quelli più comuni ci sono safe, capitalize, lower, upper, title, trim e striptags.

Test[modifica | modifica wikitesto]

Accanto ai filtri, sono disponibili i “tests” . Questi possono essere utilizzati per testare una variabile in confronto a una comune espressione. La struttura è

  {{ variabile is test }}

L'espressione regolare per gli identificatori di test è [a-Za-z_] [a-z-A-Z0-9]*(\.[a-zA-Z_][a-zA-Z0-9_]*)* .I test possono accettare anche gli argomenti. Se il test ha un solo argomento non sono necessarie le parentesi.

Costrutti di controllo[modifica | modifica wikitesto]

Una struttura di controllo si riferisce a tutto ciò che controlla il flusso di un programma: istruzione if , ciclo for, macro e blocchi. Con la sintassi di default, le strutture di controllo appaiono dentro blocchi {% … %}. Queste strutture eseguono controlli sulle variabili e l'esito di tali controlli permette di restituire parti diverse del template.

Istruzione If[modifica | modifica wikitesto]

Un'istruzione condizionale, all'interno di un template, crea un percorso decisionale. Il motore di template considera la condizione e sceglie tra due o più blocchi potenziali di codice. Il numero minimo di percorsi possibili è sempre due: uno che restituisce true se la condizione viene riscontrata; l'altro se, al contrario, la condizione non viene soddisfatta e il risultato è un blocco vuoto. L'istruzione if in Jinja2 è comparabile con quella di Python e, nella sua forma più semplice, può essere utilizzata per verificare se una variabile è definita, non vuota e non falsa, quindi come un controllo booleano. Diversamente da Python, in Jinja2 è obbligatorio terminare l'istruzione con un endif.

{% if users %}
 <ul>
    {% for user in users %}
       <li>{{ user.username|e }}</li>
    {% endfor %}
 </ul>
 {% endif %}[4]

Un'istruzione if può essere combinata con uno o più elif opzionali, per rami multipli, e con un else finale opzionale (come in Python).

 
  {% if kenny.sick %}
     Kenny is sick.
  {% elif kenny.dead %}
     You killed Kenny!  You bastard!!!
  {% else %}
     Kenny looks okay --- so far
  {% endif %}[5]

Inoltre, può essere utilizzata anche nelle espressioni inline e ciò risulta utile in alcuni casi, cioè quando non è opportuno avere più linee di codice.

 {% extends layout_template if layout_template is defined else 'master.html' %}[6]

Ciclo For[modifica | modifica wikitesto]

Cicla su ogni elemento in una sequenza, cioè il costrutto permette la creazione dinamica delle sezioni nel template ed è utile quando c’è la necessità di operare su un numero sconosciuto di elementi che devono essere trattati nello stesso modo. L'istruzione for può iterare su qualsiasi istanza iterabile e ha una sintassi molto semplice e vicina a Python. Permette anche l'uso, come in Python, di else, ma con un significato leggermente diverso. In Python il blocco else viene eseguito solo se non è preceduto da un comando break. Con Jinja2, il blocco else viene eseguito quando il for è vuoto.

 {% for i in [] %}
     {{ i }}
     {% else %} I'll be printed 
  {% endfor %}
  {% for i in [‘a'] %}
     {{ i }}
     {% else %} I won't 
  {% endfor %}[7]

Il ciclo for di Jinja2 non supporta break e continue. Per ottenere questo comportamento sono necessari dei filtri per il loop. Filtrare la sequenza durante l'iterazione consente infatti di saltare alcuni elementi:

 { for i in [1,2,3,4,5] if i >2 %}
    value: {{ i }}; loop.index: {{ loop.index }}
  {%- endfor %}[8]

Poiché le variabili nei template conservano le proprietà dei loro oggetti, è possibile eseguire iterazioni su contenitori come i dizionari. All'interno di un blocco for, si può accedere a alcune variabili speciali presentate nella seguente tabella[9]:

Variabile Descrizione
loop.index L'iterazione corrente del ciclo, indicizzato a 1
loop.index0 L'iterazione corrente del ciclo, indicizzato a 0
loop.revindex Il numero di iterazioni dalla fine del ciclo, indicizzato a 1
loop.revindex0 Il numero di iterazioni dalla fine del ciclo, indicizzato a 0
loop.first Restituisce vero alla prima iterazione
loop.last Restituisce vero all'ultima iterazione
loop.lenght Il numero di elementi nella sequenza
loop.cycle Una funzione helper per ciclare in una lista di sequenze
loop.depth Indica il livello di ricorsione corrente, partendo dal livello 1
loop.depth0 Indica il livello di ricorsione corrente, partendo dal livello 0

Macro[modifica | modifica wikitesto]

Le macro sono comparabili alle funzioni dei linguaggi di programmazione regolari. Jinja2 supporta le macro, che sono simili alle funzioni di Python, e per renderle riutilizzabili possono essere immagazzinate in file da importare. L'esempio seguente mostra una macro che restituisce un form:

  {% macro input(name, value='', type='text', size=20) -%}
       <input type="{{ type }}" name="{{ name }}" value="{{
        value|e }}" size="{{ size }}">
  {%- endmacro %}[10]

Poi la macro può essere richiamata come una funzione. Quindi, continuando con l'esempio:

  <p>{{ input('password', type='password') }}</p> 

Il form avrà quindi nome "password" ed sarà di tipo password.

Extends, Blocks, Includes[modifica | modifica wikitesto]

Tre istruzioni utili per costruire template da file diversi sono: block, extends e include.

Le prime due, block ed extends, lavorano sempre insieme. La prima viene usata per definire blocchi sovrascrivibili nel template genitore, è quindi un segnaposto e un luogo di sostituzione. La seconda richiama un template genitore con blocchi vuoti all'interno del template figlio che li eredita. Dichiara quindi che un template deriva da un altro, viene esteso perché erede di un altro.

L'istruzione include permette di restituire un template all'interno di un altro. È quindi utile per includere un template e restituire i contenuti di quel file nel namespace corrente. I template inclusi hanno accesso alle variabili del Context attivo per default. Dalla versione 2.2 di Jinja è possibile marcare un include con ignore missing: in tal caso se il template da includere non esiste viene ignorato.

Estensioni[modifica | modifica wikitesto]

Le estensioni aggiungono filtri, test e altro. Il motivo principale della creazione di estensioni è rendere una porzione di codice come fosse una classe riutilizzabile. Le estensioni vengono aggiunte all'ambiente Jinja2 al momento della creazione attraverso una lista di classi di estensioni o l'importazione. Una volta creato l'ambiente non si possono aggiungere estensioni. Le estensioni non sono abilitate per default, ma devono essere abilitate solo quando servono.

Curiosità[modifica | modifica wikitesto]

Il nome del motore deriva da quello di un tempio giapponese, poiché le parole temple/template hanno una pronuncia simile, mentre il numero che segue il nome si riferisce all'ultima versione resa disponibile[11].

Filosofia[modifica | modifica wikitesto]

La logica dell'applicazione è pensata per il controller. Tuttavia, l'obiettivo è quello di non complicare troppo la vita a colui che progetta i template, fornendogli troppe poche funzionalità.[12]

Note[modifica | modifica wikitesto]

Bibliografia[modifica | modifica wikitesto]

Fonti usate per la stesura della voce:

  • Italo Maia, Building web applications with Flask, Birmingham, Packt Publishing, 2015.
  • Miguel Grinberg, Flask Web Development: Developing Web Applications with Python, USA, O'Reilly Media, Inc., 2014.
  • Rytis Sileika, Pro Python System Administration, Apress, 2014.

Voci correlate[modifica | modifica wikitesto]

Collegamenti esterni[modifica | modifica wikitesto]