Jinja (software)

(Reindirizzamento da Jinja2)

Jinja è un motore di template scritto in Python. 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.

Jinja
software
Logo
Logo
GenereMotore di template (non in lista)
SviluppatoreArmin Ronacher
Data prima versione9 giugno 2008
Ultima versione3.1.3 (10 gennaio 2024)
Sistema operativoMultipiattaforma
LinguaggioPython
LicenzaBSD
(licenza libera)
Sito webpalletsprojects.com/p/jinja/

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

Caratteristiche principali modifica

Jinja 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.[senza fonte] Jinja 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, Jinja 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.[senza fonte]

Jinja 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 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.[non chiaro]
  • 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

Template modifica

In Jinja, 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). Jinja 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 Jinja e caricare template per l'applicazione è questo:[2]

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

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 Jinja è 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, Jinja 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

La Sandbox di Jinja 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

L'auto-escaping è una configurazione di Jinja 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 '''ciao'''. Se l'auto-escaping è abilitato, {{ x }} in un template produrrà la stringa esattamente come è data, cioè '''ciao'''. Se invece è disabilitato, che è il default di Jinja, 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

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. Jinja 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

Jinja è 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 Jinja 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

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

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. Jinja 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 Jinja 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

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_]*)* .Jinja 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

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_]*)*.[senza fonte] I test possono accettare anche gli argomenti. Se il test ha un solo argomento non sono necessarie le parentesi.

Costrutti di controllo modifica

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

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 Jinja è 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 Jinja è obbligatorio terminare l'istruzione con un endif.

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

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 %}

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' %}

Ciclo For modifica

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 Jinja, 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 %}[3]

Il ciclo for di Jinja 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 %}[3]

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:

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.length 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

Le macro sono comparabili alle funzioni dei linguaggi di programmazione regolari. Jinja 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 %}

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

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

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 Jinja 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.

Filosofia modifica

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à.[non chiaro][4]

Note modifica

Bibliografia modifica

  • 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

Collegamenti esterni modifica