# Documentazione Django 5.2.8

# Come creare tag di template e filtri personalizzati

Il linguaggio di template di Django arriva come un’ampia varietà di filtri e tag built-in progettati per far fronte alle necessità di logica di presentazione delle tue applicazioni. Eppure, potresti trovarti nella condizione di aver bisogno di funzionalità che non sono coperte dal set di primitive di template del core. Puoi estendere il motore di template definendo dei tag e filtri personalizzati usando Python e poi renderli disponibili ai tuoi template utilizzando il tag `{% load %}`

## Layout del codice

Il posto più comune in cui specificare tag e filtri di template personalizzati è nell’applicazione Django. Se sono collegati ad una app esistente, ha senso metterli lì; altrimento, possono essere aggiunti ad una nuova app. Quando una applicazione Django viene aggiunta ad `INSTALLED_APPS`, ogni tag che definisce nella locazione convenzionale descritta qui sotto viene automaticamente reso disponibile al caricamento nei template.

L’applicazione deve contenere una directory `templatetags`, allo stesso livello di `models.py`, `views.py`, ecc. Se non esiste già, creala - non dimenticare il file `__init__.py` per assicurarti che la directory sia trattata come un package Python.

I server di sviluppo non si riavviano in automatico

Dopo aver aggiunto il modulo `templatetags`, avrai bisogno di riavviare il tuo server prima di poter utilizzare i tag o i filtri nei template.

I tuoi tag e filtri personalizzati vivranno in un modulo nella directory `templatetags`. Il nome del file del modulo è il nome che userai per caricare successivamente i tag, quindi fai attenzione a sceglierne uno che non vada in conflitto con i tag ed i filtri personalizzati di un’altra applicazione.

For example, if your custom tags/filters are in a file called `poll_extras.py`, your app layout might look like this:

    polls/
        __init__.py
        models.py
        templatetags/
            __init__.py
            poll_extras.py
        views.py

E nel tuo template utilizzerai i seguenti:

    {% load poll_extras %}

Le app che contengono i tag personalizzati devono trovarsi in `INSTALLED_APPS` affinchè il tag `{% load %}` funzioni. E” una feature di sicurezza: ti rende possibile fare hosting di codice Python per molte librerie di template su una singola macchina host senza abilitare l’accesso a tutte per ogni installazione di Django.

Non c’è limite al numero di moduli che puoi aggiungere nel package `templatetags`. Tieni solo presente che uno statement `{% load %}` caricherà tag/filtri per un dato nome di modulo Python, non per il nome dell’app.

Per essere una libreria di tag valida, il modulo deve contenere una variabile module-level chiamata `register`, che è una instanza di `template.Library`, nella quale sono registrati tutti i tag ed i filtri. Così, nella parte più alta del tuo modulo, metti quanto segue:

    from django import template

    register = template.Library()

Alternativamente, i moduli per i tag di template possono essere registrati attraverso l’argomento `'libraries'` in `DjangoTemplates`. Risulta utile quando vuoi usare una label differente rispetto al nome del modulo di tag di template quando carichi i tag di template. Ti permette anche di registrare tag senza installare una applicazione.

Dietro le quinte

Per molti più esempi, leggi il codice sorgente dei filtri di default e dei tag di Django. Si trovano rispettivamente in django/template/defaultfilters.py e django/template/defaulttags.py.

Per avere più informazioni sul tag `load`, leggi la sua documentazione.

## Scrivere filtri template personalizzati

I filtri custom sono funzioni Python che prendono uno o due argomenti.

- Il valore della variabile (input) - non necessariamente una stringa.

- Il valore dell’argomento - può avere un valore di default o può essere lasciata fuori.

Per esempio, nel filtro «{{ var|foo:»bar» }}», al filtro foo sarà passata la variabile «var» e l’argomento «bar».

Dal momento che il linguaggio di template non fornisce la gestione delle eccezioni, ogni eccezione sollevata dal un filtro di template sarà esposta come errore del server. Quindi, le funzioni di filtro dovrebbero evitare di sollevare eccezioni se c’è un valore di fallback ragionevole da restituire. In caso di input che rappresenti un chiaro bug in un template, sollevare una eccezione può ancora essere meglio di un fallimento silente che nasconde il bug.

Un esempio della definizione di un filtro

    def cut(value, arg):
        """Removes all values of arg from the given string"""
        return value.replace(arg, "")

E qui un esempio di come il viene usato

    {{ somevariable|cut:"0" }}

Moltri filtri non prendono argomenti. In questo caso, lascia l’argomento fuori dalla tua funzione:

    def lower(value):  # Only one argument.
        """Converts a string into all lowercase"""
        return value.lower()

### Registrazione di filtri personalizzati

django.template.Library.filter()

Dopo aver scritto la definizione del filtro, bisogna registrarlo con l’istanza di «Library» al fine di renderlo disponibile per l’uso nel linguaggio dei template di Django.

    register.filter("cut", cut)
    register.filter("lower", lower)

Il metodo «Library.filter()» prende due argomenti:

1. Il nome del filtro – una stringa

2. La funzione di compilazione –una funzione Python (non il nome di funzione come stringa).

In alternativa puoi usare `register.filter()` come decorator

    @register.filter(name="cut")
    def cut(value, arg):
        return value.replace(arg, "")

    @register.filter
    def lower(value):
        return value.lower()

Se lasci fuori l’argomento `name`, come nell’esempio sopra, Django userà il nome della funzione come nome di filtro.

Infine, `register.filter()` accetta anche tre argomenti keyword, `is_safe`, `needs_autoescape` e `expects_localtime`. Questi argomenti sono descritti in filtri ed auto-escaping e filtri e time zones qui sotto.

### Template filtri che si aspettano delle stringhe

django.template.defaultfilters.stringfilter()

Se stai scrivendo un filtro di template che si aspetta solo una stringa come primo argomento, dovresti utilizzare il decoratore `stringfilter`. Questo convertirà un oggetto al suo valore stringa prima di passarlo alla tua funzione:

    from django import template
    from django.template.defaultfilters import stringfilter

    register = template.Library()

    @register.filter
    @stringfilter
    def lower(value):
        return value.lower()

In questo modo, sarà in grado di passare, diciamo, un intero a questo filtro e non causerà un `AttributeError` (perchè gli integer non hanno metodi `lower()`)

### Filtri e auto escaping

Scrivendo un filtro personalizzato, pensa a come il filtro interagirà con i comportamenti di auto-escaping di Django. Nota che nel codice di template possono essere passati due tipi di stringhe:

- Le Raw strings sono le stringhe native di Python. All’output, sono oggetto di escape se è attivo l’auto-escape o altrimenti presentate senza cambiamenti.

- Le Safe strings sono stringhe che sono state marcate come safe da ulteriori escape a tempo di output. Ogni escaping necessario è già stato fatto. Sono usate comunemente per output che contiene HTML grezzo che deve essere interpretato così come è client-side.

  Internamente, queste stringhe sono di tipo `SafeString`. Puoi testarle utilizzando del codice come questo:

      from django.utils.safestring import SafeString

      if isinstance(value, SafeString):
          # Do something with the "safe" string.
          ...

Il codice dei filtri di template ricade in una delle due situazioni:

1. Il tuo filtro non introduce caratteri HTML non sicuri (`<`, `>`, `'`, `"` or `&`) nei risultati che non erano già presenti. In questo caso, puoi lasciare che Django si curi di fare auto-escape per te. Tutto quel che devi fare è impostare `is_safe` a `True` quando registri la funzione di filtro, così:

       @register.filter(is_safe=True)
       def myfilter(value):
           return value

   Questo flag dice a Django che se una striga «safe» viene passata nel filtro, il risultato sarà ancora «safe» e se viene passata una stringa «non safe», Django farà automaticamente escape, se necessario.

   Puoi pensarlo come «questo filtro è sicuro – non introduce alcuna possibilità di HTML non safe».

   La ragione per la quale `is_safe` è necessario è perchè ci sono molte operazioni sulle stringhe che trasformano un oggetto `SafeData` in uno oggetto `str` normale e, piuttosto che cercare di catturarle tutte, cosa che sarebbe molto difficile, Django ripara il danno dopo aver completato il filtro.

   Per esempio, supponi di avere un filtro che aggiunge `xx` alla fine di ogni input. Siccome non introduce caratteri HTML pericolosi al risultato (tranne che nel caso non ci siano già), dovresti marcare il tuo filtro con `is_safe`:

       @register.filter(is_safe=True)
       def add_xx(value):
           return "%sxx" % value

   Quando questo filtro viene utilizzato in un template quando l’auto-escape è abilitato, Django farà escape dell’output in tutti i casi in cui l’input non sia marcato come «safe».

   Di default, `is_safe` è `False` e puoi ometterlo da tutti i filtri in cui non sia required.

   Fai attenzione quando decidi se il tuo filtro davvero lascia le stringhe safe come safe. Se stai rimuovendo caratteri, potresti lasciare inavvertitamente tag HTML non bilanciati o entità nei risultati. Per esempio, rimuovere un `>` dall’input potrebbe convertire `1` in `<a`, che avrebbe bisogno di escaping sull’output per evitare problemi. In modo simile, rimuovere un punto e virgola (`;`) può trasformare `&amp;` in `&amp`, che non è più una entità valida e quindi necessità di ulteriore escaping. In molti casi non sarà così complicato ma fai attenzione a problemi come questi quando rivedi il tuo codice.

   Marcare un filtro come `is_safe` forzerà il valore restituito a stringa. Se il tuo filtro dovrebbe restituire un booleano o altri valori non stringa, marcarlo come `is_safe` probabilmente avrà conseguenze non volute (come la conversione del booleano False nella stringa “False”).

2. Alternativamente, il tuo codice può occuparsi manualmente di ogni escaping necessario. Questo è necessario quando introduci del nuovo markup HTML nei risultati. Potresti voler marcare l’output come safe così da non introdurre ulteriore escaping ed in questo caso dovrai occuparti da te dell’input.

   Per marcare l’output come stringa safe, usa `django.utils.safestring.mark_safe()`.

   Stai attento però. Devi fare altro oltre a marcare l’output come safe. Devi assicurarti che sia davvero safe e quel che fai dipende dal fatto che l’auto-escaping sia attivo. L’idea è scrivere filtri che possano operare in template in cui l’auto-escape sia attivo o meno per rendere più facili le cose agli autori dei template.

   Perchè il filtro conosca lo stato corrente di auto-escape, imposta il flag `needs_autoescape` a `True` quando registri la tua funzione di filtro (se non lo specifichi, di default è impostato a `False`). Questo flag dice a Django che il tuo filtro vuole che gli sia passato un argomento extra chiamato `autoescape`, che è `True` se l’auto-escape è attivo e `False` altrimenti. Si raccomanda di impostare il default sull’auto-escape a `True`, così che chiamando la funzione dal codice Python l’escape sarà abilitato di default.

   Ad esempio, scriviamo un filtro che enfatizza il primo carattere di una stringa:

       from django import template
       from django.utils.html import conditional_escape
       from django.utils.safestring import mark_safe

       register = template.Library()

       @register.filter(needs_autoescape=True)
       def initial_letter_filter(text, autoescape=True):
           first, other = text[0], text[1:]
           if autoescape:
               esc = conditional_escape
           else:
               esc = lambda x: x
           result = "<strong>%s</strong>%s" % (esc(first), esc(other))
           return mark_safe(result)

   Il flag `needs_autoescape` e l’argomento keyword `autoescape` fanno sì che la funzione sappia quando l’escaping automatico è attivo nel momento in cui il filtro viene chiamato. Usiamo `autoescape` per decidere quando l’input debba essere passato in `django.utils.html.conditional_escape` o meno. (In quest’ultimo caso, usiamo la funzione identità come funzione di «escape»). La funzione `conditional_escape()` è come `escape()` a parte per il fatto che fa escaping dell’input che non è una instanza di `SafeData`. Se una istanza di `SafeData` viene passata a `conditional_escape()`, i dati vengono restituiti così come sono.

   Infine, nell’esempio sopra, ci ricordiamo di marcare il risultato come safe in modo che il nostro HTML venga inserito direttamente nel template senza ulteriore escaping.

   Non c’è bisogno di preoccuparsi del flag `is_safe` in questo caso (anche se includerlo non farebbe male). Ogni volta che gestisci manualmente i problemi di auto-escape e restituisci una stringa safe, il flag `is_safe` non cambierebbe comunque niente.

Avvertimento

Evitare le vulnerabilità XSS quando si riutilizzano i filtri built-in

I filtri built-in di Django hanno di default «autoescape=True» al fine di assicurare il corretto autoescaping ed evitare vulnerabilità di tipo cross-site script.

Nelle vecchie versioni di Django, fai attenzione quando riusi i filtri built-in di Django perchè `autoescape` viene impostato di default a `None`. Dovrai passare `autoescape=True` per fare autoescaping.

Per esempio, se volessi scrivere un filtro custom chiamato `urlize_and_linebreaks` che combina i filtri `urlize` e `linebreaksbr`, avrebbe questo aspetto:

    from django.template.defaultfilters import linebreaksbr, urlize

    @register.filter(needs_autoescape=True)
    def urlize_and_linebreaks(text, autoescape=True):
        return linebreaksbr(urlize(text, autoescape=autoescape), autoescape=autoescape)

Poi

    {{ comment|urlize_and_linebreaks }}

sarebbe equivalente a:

    {{ comment|urlize|linebreaksbr }}

### Filtri e time zones

Se scrivessi un filtro custom che opera sugli oggetti `datetime`, normalmente lo registreresti con il flag `expects_localtime` a `True`:

    @register.filter(expects_localtime=True)
    def businesshours(value):
        try:
            return 9 <= value.hour < 17
        except AttributeError:
            return ""

Quando questo flag è impostato, se il primo argomento del tuo filtro è un datetime con time zone, Django lo converte alla time zone corrente prima di passarlo al tuo filtro ove appropriato, secondo le regole per le conversioni delle time zone nei template.

## Scrivere template tag personalizzati

I tag sono più complessi dei filtri, perchè i tag possono fare qualsiasi cosa. Django offre degli shortcut che rendono la scrittura di molti tipi di tag più semplice. Prima esploreremo questi shortcut, poi spiegheremo come scrivere un tag da zero per quei casi in cui gli shortcut non bastano.

### Semplici tag

django.template.Library.simple_tag()

Molti template tag prendono un certo numero di argomenti – stringhe o variabili di template – e restituiscono un risultato dopo aver fatto qualche processamento basato esclusivamente sugli argomenti di input e qualche informazione esterna. Per esempio, un tag `current_time` potrebbe accettare una stringa di formattazione e restituire l’ora come stringa formatta di conseguenza.

Per facilitare la creazione di questi tipi di tag, Django fornisce una funzione helper, `simple_tag`. Questa funzione, che è un metodo di `django.template.Library`, prende una funzione che accetta un numero arbitrario di argomenti, la racchiude in una funzione `render` e le altre parti necessarie menzionate prima e la registra nel sistema di template.

La nostra funzione `current_time` dovrebbe essere scritta così:

    import datetime
    from django import template

    register = template.Library()

    @register.simple_tag
    def current_time(format_string):
        return datetime.datetime.now().strftime(format_string)

Alcune cose da notare a proposito della funzione helper `simple_tag`:

- Controllare il numero richiesto di argomenti ecc…viene già fatto quando la funzione viene chiamata, quindi non dobbiamo farlo.

- Le virgolette attorno all’argomento (se ce ne sono) sono già state eliminate, quindi riceviamo una stringa semplice.

- Se l’argomento era una variabile di template, alla nostra funzione viene passato il valore corrente della variabile, non la variabile stessa.

A differenza di altre utilità di tag, `simple_tag` passa il suo output attraverso `conditional_escape()` se il template context è in modalità autoescape, per assicurare un HTML corretto e proteggerti da vulnerabilità XSS.

Se non si desidera un escaping aggiuntivo, sarà necessario utilizzare `mark_safe()` solo se si è assolutamente sicuri che il codice non contenga vulnerabilità XSS. Per creare piccoli frammenti di codice HTML, è fortemente raccomandato l’uso di `format_html()` invece di `mark_safe()`.

Se il tuo tag nel template ha bisogno di accedere al context corrente, puoi usare l’argomento `takes_context` quando registri il tuo tag:

    @register.simple_tag(takes_context=True)
    def current_time(context, format_string):
        timezone = context["timezone"]
        return your_get_current_time_method(timezone, format_string)

Nota che il primo argomento deve chiamarsi «context»

Per ulteriori informazioni su come funziona l’opzione `takes_context` vedere la sezione su inclusion tags.

Se hai bisogno dirinominare il tag, puoi fornirne un nome personalizzato:

    register.simple_tag(lambda x: x - 1, name="minusone")

    @register.simple_tag(name="minustwo")
    def some_function(value):
        return value - 2

Le funzioni `simple_tag` possono accettare un numero qualsiasi di argomenti posizionali o di parole chiave. Per esempio:

    @register.simple_tag
    def my_tag(a, b, *args, **kwargs):
        warning = kwargs["warning"]
        profile = kwargs["profile"]
        ...
        return ...

Quindi nel template qualsiasi argomento, separato da spazi, può essere passato al tag del template. Come in Python, i valori per gli argomenti delle parole chiave sono impostati usando il segno di uguale (»`=`») e devono essere inseriti dopo gli argomenti posizionali. Per esempio:

    {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}

È possibile memorizzare i risultati del tag in una variabile del template anziché esporlo direttamente. Questo viene fatto usando l’argomento `as` seguito dal nome della variabile. In questo modo puoi mostrare il contenuto dove ritieni opportuno:

    {% current_time "%Y-%m-%d %I:%M %p" as the_time %}
    <p>The time is {{ the_time }}.</p>

### Simple block tags

New in Django 5.2.

django.template.Library.simple_block_tag()

When a section of rendered template needs to be passed into a custom tag, Django provides the `simple_block_tag` helper function to accomplish this. Similar to `simple_tag()`, this function accepts a custom tag function, but with the additional `content` argument, which contains the rendered content as defined inside the tag. This allows dynamic template sections to be easily incorporated into custom tags.

For example, a custom block tag which creates a chart could look like this:

    from django import template
    from myapp.charts import render_chart

    register = template.Library()

    @register.simple_block_tag
    def chart(content):
        return render_chart(source=content)

The `content` argument contains everything in between the `{% chart %}` and `{% endchart %}` tags:

    {% chart %}
      digraph G {
          label = "Chart for {{ request.user }}"
          A -> {B C}
      }
    {% endchart %}

If there are other template tags or variables inside the `content` block, they will be rendered before being passed to the tag function. In the example above, `request.user` will be resolved by the time `render_chart` is called.

Block tags are closed with `end{name}` (for example, `endchart`). This can be customized with the `end_name` parameter:

    @register.simple_block_tag(end_name="endofchart")
    def chart(content):
        return render_chart(source=content)

Which would require a template definition like this:

    {% chart %}
      digraph G {
          label = "Chart for {{ request.user }}"
          A -> {B C}
      }
    {% endofchart %}

A few things to note about `simple_block_tag`:

- The first argument must be called `content`, and it will contain the contents of the template tag as a rendered string.

- Variables passed to the tag are not included in the rendering context of the content, as would be when using the `{% with %}` tag.

Just like simple_tag, `simple_block_tag`:

- Validates the quantity and quality of the arguments.

- Strips quotes from arguments if necessary.

- Escapes the output accordingly.

- Supports passing `takes_context=True` at registration time to access context. Note that in this case, the first argument to the custom function must be called `context`, and `content` must follow.

- Supports renaming the tag by passing the `name` argument when registering.

- Supports accepting any number of positional or keyword arguments.

- Supports storing the result in a template variable using the `as` variant.

Content Escaping

`simple_block_tag` behaves similarly to `simple_tag` regarding auto-escaping. For details on escaping and safety, refer to `simple_tag`. Because the `content` argument has already been rendered by Django, it is already escaped.

#### A complete example

Consider a custom template tag that generates a message box that supports multiple message levels and content beyond a simple phrase. This could be implemented using a `simple_block_tag` as follows:

`testapp/templatetags/testapptags.py`

    from django import template
    from django.utils.html import format_html

    register = template.Library()

    @register.simple_block_tag(takes_context=True)
    def msgbox(context, content, level):
        format_kwargs = {
            "level": level.lower(),
            "level_title": level.capitalize(),
            "content": content,
            "open": " open" if level.lower() == "error" else "",
            "site": context.get("site", "My Site"),
        }
        result = """
        <div class="msgbox {level}">
          <details{open}>
            <summary>
              <strong>{level_title}</strong>: Please read for <i>{site}</i>
            </summary>
            <p>
              {content}
            </p>
          </details>
        </div>
        """
        return format_html(result, **format_kwargs)

When combined with a minimal view and corresponding template, as shown here:

`testapp/views.py`

    from django.shortcuts import render

    def simpleblocktag_view(request):
        return render(request, "test.html", context={"site": "Important Site"})

`testapp/templates/test.html`

    {% extends "base.html" %}

    {% load testapptags %}

    {% block content %}

      {% msgbox level="error" %}
        Please fix all errors. Further documentation can be found at
        <a href="http://example.com">Docs</a>.
      {% endmsgbox %}

      {% msgbox level="info" %}
        More information at: <a href="http://othersite.com">Other Site</a>/
      {% endmsgbox %}

    {% endblock %}

The following HTML is produced as the rendered output:

    <div class="msgbox error">
      <details open>
        <summary>
          <strong>Error</strong>: Please read for <i>Important Site</i>
        </summary>
        <p>
          Please fix all errors. Further documentation can be found at
          <a href="http://example.com">Docs</a>.
        </p>
      </details>
    </div>

    <div class="msgbox info">
      <details>
        <summary>
          <strong>Info</strong>: Please read for <i>Important Site</i>
        </summary>
        <p>
          More information at: <a href="http://othersite.com">Other Site</a>
        </p>
      </details>
    </div>

### Tag di inclusione

django.template.Library.inclusion_tag()

Un altro tipo comune di tag template è il tipo che visualizza alcuni dati eseguendo il rendering di un altro template. Ad esempio, l’interfaccia di amministrazione di Django utilizza tag template personalizzati per visualizzare i pulsanti «aggiungi/modifica» nella parte inferiore delle pagine modulo (form). Quei pulsanti hanno sempre lo stesso aspetto, ma le destinazioni del collegamento cambiano a seconda dell’oggetto che viene modificato, quindi sono un caso perfetto per l’utilizzo di un piccolo template pieno di dettagli dall’oggetto corrente. (Nel caso dell’amministratore, questo è il tag `submit_row`.)

I tag di questo tipo sono detti «tag di inclusione»

La scrittura di tag di inclusione è probabilmente meglio dimostrata dall’esempio. Scriviamo un tag che produca un elenco di scelte per un dato oggetto `Poll`, come è stato creato in tutorials. Useremo il tag in questo modo:

    {% show_results poll %}

… e l’output sarà qualcosa di simile a questo:

    <ul>
      <li>First choice</li>
      <li>Second choice</li>
      <li>Third choice</li>
    </ul>

Innanzitutto, definisci la funzione che accetta l’argomento e produce un dizionario di dati per il risultato. Il punto importante qui è che dobbiamo solo restituire un dizionario, non qualcosa di più complesso. Questo verrà utilizzato come context del template per il frammento del template. Esempio:

    def show_results(poll):
        choices = poll.choice_set.all()
        return {"choices": choices}

Quindi, crea il template utilizzato per eseguire il rendering dell’output dei tag. Questo modello è una caratteristica fissa dei tag: lo specifica l’autore del tag, non il designer del template. Seguendo il nostro esempio, il template è molto breve:

    <ul>
    {% for choice in choices %}
        <li> {{ choice }} </li>
    {% endfor %}
    </ul>

Ora, crea e registra il tag di inclusione chiamando il metodo `inclusion_tag()` su un oggetto `Library`. Seguendo il nostro esempio, se il template sopra si trova in un file chiamato `results.html` in una directory che viene cercata dall’invocatore del template, registreremo il tag in questo modo:

    # Here, register is a django.template.Library instance, as before
    @register.inclusion_tag("results.html")
    def show_results(poll): ...

In alternativa è possibile registrare il tag di inclusione utilizzando un’istanza di `django.template.Template`:

    from django.template.loader import get_template

    t = get_template("results.html")
    register.inclusion_tag(t)(show_results)

…alla prima creazione della funzione.

A volte, i tag di inclusione potrebbero richiedere un numero elevato di argomenti, rendendo difficile per gli autori dei template passare tutti gli argomenti e ricordare il loro ordine. Per risolvere questo problema, Django fornisce un’opzione `takes_context` per i tag di inclusione. Se specifichi `takes_context` nella creazione di un tag nel template, il tag non avrà argomenti richiesti e la funzione Python sottostante avrà un argomento – il context del template a partire da quando il tag è stato chiamato.

Ad esempio, supponiamo che tu stia scrivendo un tag di inclusione che sarà sempre utilizzato in un context che contiene variabili `home_link` e `home_title` che puntano alla pagina principale. Ecco come sarebbe la funzione Python:

    @register.inclusion_tag("link.html", takes_context=True)
    def jump_link(context):
        return {
            "link": context["home_link"],
            "title": context["home_title"],
        }

Si noti che il primo parametro della funzione deve essere chiamato `context`.

In quella riga `register.inclusion_tag()`, abbiamo specificato `takes_context=True` e il nome del template. Ecco come potrebbe apparire il template `link.html`:

    Jump directly to <a href="{{ link }}">{{ title }}</a>.

Quindi, ogni volta che desideri utilizzare quel tag personalizzato, carica la sua libreria e chiamala senza argomenti, in questo modo:

    {% jump_link %}

Nota che quando usi `takes_context=True`, non è necessario passare argomenti al tag del modello. Ottiene automaticamente l’accesso al context.

Il parametro `takes_context` è predefinito su `False`. Quando è impostato su `True`, al tag viene passato l’oggetto context, come in questo esempio. Questa è l’unica differenza tra questo caso e il precedente esempio di `inclusion_tag`.

Le funzioni `inclusion_tag` possono accettare qualsiasi argomento posizionale o di parole chiave. Per esempio:

    @register.inclusion_tag("my_template.html")
    def my_tag(a, b, *args, **kwargs):
        warning = kwargs["warning"]
        profile = kwargs["profile"]
        ...
        return ...

Quindi nel template qualsiasi argomento, separato da spazi, può essere passato al tag del template. Come in Python, i valori per gli argomenti delle parole chiave sono impostati usando il segno di uguale (»`=`») e devono essere inseriti dopo gli argomenti posizionali. Per esempio:

    {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}

### Tag di template personalizzati avanzati

A volte le funzionalità di base per la creazione di tag in template personalizzati non sono sufficienti. Non preoccuparti, Django ti dà accesso completo al necessario per creare un tag template da zero.

### Una rapida panoramica

Il sistema di template funziona in un processo in due fasi: compilazione e rendering. Per definire un tag template personalizzato, specifica come funziona la compilation e come funziona il rendering.

Quando Django compila un template, divide il testo del template grezzo in “”nodi””. Ogni nodo è un’istanza di `django.template.Node` e ha un metodo `render()`. Un template compilato è un elenco di oggetti `Nodo`. Quando chiami `render()` su un oggetto del template compilato, il template chiama `render()` su ogni `Nodo` nella sua lista dei nodi, con il context fornito. I risultati sono tutti concatenati insieme per formare il risultato (output) del template.

Quindi, per definire un custom template tag: specifica come il template tag grezzo viene convertito in un `Node` (usa la funzione di compilazione) e definisci cosa fa il metodo `render()` del nodo.

### Scrivere la funzione di compilazione

Per ogni tag del template che incontra il parser del template, chiama una funzione Python con il contenuto del tag e l’oggetto del parser stesso. Questa funzione è responsabile della restituzione di un’istanza `Node` basata sul contenuto del template tag.

Per esempio, scriviamo un’implementazione completa del nostro tag template, `{% current_time %}`, che mostra la data/ora corrente, formattata secondo un parametro dato nel tag, sintassi :func:`~time.strftime ` . È una buona idea decidere la sintassi dei template tag prima di ogni altra cosa. Nel nostro caso, supponiamo che il tag debba essere utilizzato in questo modo:

    <p>The time is {% current_time "%Y-%m-%d %I:%M %p" %}.</p>

Il parser per questa funzione dovrebbe prendere il parametro e creare un oggetto `Node`:

    from django import template

    def do_current_time(parser, token):
        try:
            # split_contents() knows not to split quoted strings.
            tag_name, format_string = token.split_contents()
        except ValueError:
            raise template.TemplateSyntaxError(
                "%r tag requires a single argument" % token.contents.split()[0]
            )
        if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):
            raise template.TemplateSyntaxError(
                "%r tag's argument should be in quotes" % tag_name
            )
        return CurrentTimeNode(format_string[1:-1])

Note:

- `parser` è l’oggetto parser di template. Non ne abbiamo bisogno in questo esempio.

- `token.contents` è una stringa di contenuti grezzi del tag. Nel nostro esempio, è `'current_time "%Y-%m-%d %I:%M %p"'`.

- Il metodo `token.split_contents()` separa gli argomenti con gli spazi tenendo assieme le stringhe tra virgolette. Il più diretto `token.contents.split()` non sarebbe così robusto, perchè dividerebbe pedissequamente tutti gli spazi, inclusi quelli tra virgolette. E” una buona idea usare sempre token.split_contents()`.

- Questa funzione è responsabile di sollevare `django.template.TemplateSyntaxError`, con messaggi utili, per ogni errore di sintassi.

- Le eccezioni `TemplateSyntaxError` usano la variabile `tag_name`. Non fare hard-coding del nome del tag nei tuoi messaggi di errore, perchè questo accoppia il nome del tuo tag alla funzione. `token.contents.split()[0]` sarò «sempre» il nome del tuo tag – anche quando il tag non prende argomenti.

- La funzione restituisce un `CurrentTimeNode` con tutto ciò di cui il nodo ha bisogno di sapere circa questo tag. In questo caso, passa l’argomento –`"%Y-%m-%d %I:%M %p"`. Le virgolette in testa ed in coda dal tag di template sono rimosse in `format_string[1:-1]`.

- Il parsing è molto di basso livello. Gli sviluppatori di Django hanno sperimentato nello scrivere piccoli framework su questo sistema di parsing, usando tecniche con le grammatiche EBNF, ma questi esperimenti hanno reso il motore di template troppo lento. E” di basso livello perchè in questo modo è più veloce.

### Scrivere il renderer

Il secondo passo nello scrivere tag personalizzati è definire una sottoclasse `Node` che ha un metodo `render()`.

Continuando l’esempio precedente, dobbiamo definire `CurrentTimeNode`:

    import datetime
    from django import template

    class CurrentTimeNode(template.Node):
        def __init__(self, format_string):
            self.format_string = format_string

        def render(self, context):
            return datetime.datetime.now().strftime(self.format_string)

Note:

- `__init__()` prende `format_string` da `do_current_time()`. Passa sempre qualsiasi opzione/parametro/argomento ad un `Node` tramite il suo `__init__()`.

- Il metodo `render()` è dove viene fatto il lavoro vero e proprio.

- `render()` generalmente dovrebbe fallire in modo silenzioso, in particolare in un ambiente di produzione. In qualche caso tuttavia, specialmente se `context.template.engine.debug` è impostato a `True`, questo metodo può sollevare una eccezione per facilitare il debugging. Per esempio, molti tag core sollevano `django.template.TemplateSyntaxError` se ricevono un numero di argomenti sbagliato.

In ultimo, questo disaccoppiamento tra compilazione e rendering ha come risultato un sistema di template efficiente, perchè un template può fare render di diversi contesti senza dover essere analizzato più volte.

### Considerazioni sull’auto-escape

L’output da template tag non è automaticamente passato ai filtri di auto-escape (con l’eccezione di `simple_tag()` come descritto sopra). Comunque, ci sono ancora un paio di cose che dovresti tenere a mente quando scrivi un tag di template.

Se il metodo `render()` del tuo template tag immagazzina il risultato in una variabile di contesto (piuttosto che restituirlo in una stringa), dovrebbe fare attenzione a chiamare `mark_safe()` quando è opportuno. Quando la variabile viene resa, alla fine, in quel momento sarà influenzata dalle impostazioni di auto-escape, così il contenuto che dovrebbe essere sicuro rispetto ad un ulteriore escaping dovrebbe essere marcato come tale.

Inoltre, se il tuo tag di template crea un nuovo contesto per fare qualche sotto-rendering, imposta l’attributo di auto-escape al valore corrente di contesto. Il metodo `__init__` per la classe `Context` prende un parametro chiamato `autoescape` che puoi usare per questo scopo. Per esempio:

    from django.template import Context

    def render(self, context):
        # ...
        new_context = Context({"var": obj}, autoescape=context.autoescape)
        # ... Do something with new_context ...

Non è una situazione molto comune, ma è utile se si fa rendering di un template da soli. Per esempio:

    def render(self, context):
        t = context.template.engine.get_template("small_fragment.html")
        return t.render(Context({"var": obj}, autoescape=context.autoescape))

Se non ci fossimo curati di passare il valore `context.autoescape` al nostro nuovo `Context` in questo esempio, sui risultati sarebbe sempre stato fatto automaticamente escape, il che avrebbe potuto non essere il comportamento desiderato se il tag di template fosse stato usato in un blocco `{% autoescape off %}`

### Considerazioni sulla sicurezza

Una volta che un nodo viene analizzato, il suo metodo `render` può essere chiamato un numero indefinito di volte. Dal momento che Django ogni tanto gira in ambienti multi-threaded, un singolo nodo può essere simultaneamente reso con differenti contesti in risposta a richieste differenti. Quindi, è importante assicurarsi che i propri template tag siano thread safe.

Per essere sicuro che i template tag siano thread safe, non dovresti mai immagazzinare informazioni di stato sul nodo stesso. Per esempio, Django offre un template tag builtin `cycle` che compie un ciclo lungo una lista di date stringhe ogni volta che viene visualizzato:

    {% for o in some_list %}
        <tr class="{% cycle 'row1' 'row2' %}">
            ...
        </tr>
    {% endfor %}

Una implementazione naive di `CycleNode` può avere un aspetto simile:

    import itertools
    from django import template

    class CycleNode(template.Node):
        def __init__(self, cyclevars):
            self.cycle_iter = itertools.cycle(cyclevars)

        def render(self, context):
            return next(self.cycle_iter)

Ma, supponiamo di avere due template che visualizzano lo snippet di template qui sopra allo stesso tempo:

1. Il thread 1 esegue la sua prima iterazione di loop, `CycleNode.render()` restituisce “row1”

2. Il thread 2 esegue la sua prima iterazione di loop, `CycleNode.render()` restituisce “row2”

3. Il thread 1 esegue la sua seconda iterazione di loop, `CycleNode.render()` restituisce “row1”

4. Il thread 2 esegue la sua seconda iterazione di loop, `CycleNode.render()` restituisce “row2”

CycleNode sta iterando, ma sta iterando globalmente. Per quel che ne sanno Thread 1 e Thread 2, sta restituendo sempre lo stesso valore. Non è quello che vogliamo!

Per risolvere questo problema, Django offre un `render_context` che è associato con il `context` del template che si mostra in quel momento. `render_context` si comporta come un dizionario Python e dovrebbe essere usato per memorizzare lo stato di `Node` tra le invocazioni del metodo `render`.

Rifattorizziamo la nostra implementazione di `CycleNode` per usare `render_context`:

    class CycleNode(template.Node):
        def __init__(self, cyclevars):
            self.cyclevars = cyclevars

        def render(self, context):
            if self not in context.render_context:
                context.render_context[self] = itertools.cycle(self.cyclevars)
            cycle_iter = context.render_context[self]
            return next(cycle_iter)

Nota che è perfettamente sicuro memorizzare l’informazione globale che non cambierà attraverso il ciclo di vita del `Node` come attributo. Nel caso di `CycleNode`, l’argomento `cyclevars` non cambia dopo che `Node` viene istanziato, quindi non abbiamo bisogno di metterlo in `render_context`. Ma l’informazione di stato che è specifica del template che si sta presentando, come l’iterazione corrente di `CycleNode`, dovrebbe essere memorizzata in `render_context`.

Nota

Nota come abbiamo usato `self` per dare uno scope alla specifica informazione `CycleNode` nel `render_context`. Ci possono essere molteplici `CycleNodes` in un dato template, quindi abbiamo bisogno di stare attenti a non andare in conflitto con l’informazione di stato di un altro nodo. Il modo più semplice di farlo è usare sempre `self` come chiave in `render_context`. Se stai tenendo traccia di diverse variabili di stato, rendi `render_context[self]` un dizionario.

### Registrare il tag

Infine, registra il tag nell’istanza del tuo modulo `Library`, come spiegato in scrivere tag di template personalizzati qui sopra. Per esempio:

    register.tag("current_time", do_current_time)

Il methodo `tag()` ha bisogni di due argomenti:

1. Il nome del template tag – una stringa. Se viene tralasciato, il nome della fu..?

2. La funzione di compilazione –una funzione Python (non il nome di funzione come stringa).

Così come con la registrazione di un filtro, è anche possibile usarlo come decoratore:

    @register.tag(name="current_time")
    def do_current_time(parser, token): ...

    @register.tag
    def shout(parser, token): ...

Se non specifichi l’argomento `name`, come nel secondo esempio qui sopra, Django userà il nome della funzione come nome del tag.

### Passare variabili del template ai tag

Anche se puoi passare un numero arbitrario di argomenti ad un tag di template usando `token.split_contents()`, gli argomenti sono tutti spacchettati come string literals. E” richiesto un po” più di lavoro per passare un contenuto dinamico (una variabile di template) come argomento ad un tag di template.

Mentre gli esempi precedenti hanno formattato l’ora corrente in una stringa e restituito una stringa, supponi di voler passare un `DateTimeField` da un oggetto ed avere il template tag che formatta il date-time:

    <p>This post was last updated at {% format_time blog_entry.date_updated "%Y-%m-%d %I:%M %p" %}.</p>

All’inizio, `token.split_contents()` restituirà tre valori:

1. Il nome tag `format_time`.

2. La stringa `'blog_entry.date_updated'` (senza le virgolette).

3. La stringa di formattazione `'"%Y-%m-%d %I:%M %p"'`. Il valore restituito da `split_contents()` includerà le virgolette in capo ed in coda agli string literals come questo.

Adesso il tuo tag comincia a sembrare così:

    from django import template

    def do_format_time(parser, token):
        try:
            # split_contents() knows not to split quoted strings.
            tag_name, date_to_be_formatted, format_string = token.split_contents()
        except ValueError:
            raise template.TemplateSyntaxError(
                "%r tag requires exactly two arguments" % token.contents.split()[0]
            )
        if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):
            raise template.TemplateSyntaxError(
                "%r tag's argument should be in quotes" % tag_name
            )
        return FormatTimeNode(date_to_be_formatted, format_string[1:-1])

Devi anche cambiare il renderer per ritirare il contenuto attuale della proprietà `date_updated` dell’oggetto `blog_entry`. Questo può essere ottenuto usando la classe `Variable()` in `django.template`.

Per usare la classe `Variable`, instanziala con il nome della variabile da risolvere, e poi chiama `variable.resolve(context)`. Quindi, per esempio:

    class FormatTimeNode(template.Node):
        def __init__(self, date_to_be_formatted, format_string):
            self.date_to_be_formatted = template.Variable(date_to_be_formatted)
            self.format_string = format_string

        def render(self, context):
            try:
                actual_date = self.date_to_be_formatted.resolve(context)
                return actual_date.strftime(self.format_string)
            except template.VariableDoesNotExist:
                return ""

La risoluzione della variabile solleverà una eccezione `VariableDoesNotExist` se non può risolvere la stringa che gli si passa nel contesto corrente della pagina.

### Impostare una variabile nel contesto

L’esempio qui sopra mostra un valore. Generalmente, è tutto più flessibile se i template tag impostano le variabili di template invece di mostrare valori. In questo modo, gli autori di template possono riusare i valori che i tuoi tag di template creano.

Per impostare una variabile nel contesto, usa l’assegnazione di dizionario sull’oggetto context nel metodo `render()`. Ecco una versione aggiornata di `CurrentTimeNode` che imposta una variabile di template chiamata `current_time` invece di mostrarla.

    import datetime
    from django import template

    class CurrentTimeNode2(template.Node):
        def __init__(self, format_string):
            self.format_string = format_string

        def render(self, context):
            context["current_time"] = datetime.datetime.now().strftime(self.format_string)
            return ""

Nota che `render()` restituisce una stringa vuota. `render()` dovrebbe sempre restituire un output come stringa. Se tutto quel che il tag di template fa è impostare una variabile, `render()` dovrebbe restituire una stringa vuota.

Ecco come useresti la nuova versione del tag:

    {% current_time "%Y-%m-%d %I:%M %p" %}<p>The time is {{ current_time }}.</p>

Scope delle variabili nel contesto

Ogni variabile impostata nel context sarà solo disponibile nello stesso `blocco` del template in cui è stata assegnata. Questo comportamento è intenzionale; fornisce uno scope per le variabili, in modo che non vadano in conflitto con il context in altri blocchi.

Ma c’è un problema con `CurrentTimeNode2`: il nome di variabile `current_time` è hard-coded. Questo significa che dovrai accertarti che il tuo template non usi `{{ current_time }}` da nessun altra parte, perchè `{% current_time %}` sovrascriverà ciecamente quel nome di variabile. Una soluzione più pulita è fare in modo che il tag di template specifichi il nome della variabile di output, in questo modo:

    {% current_time "%Y-%m-%d %I:%M %p" as my_current_time %}
    <p>The current time is {{ my_current_time }}.</p>

Per fare ciò, dovrai rifattorizzare sia la funzione di compilazione che la classe `Node`, così:

    import re

    class CurrentTimeNode3(template.Node):
        def __init__(self, format_string, var_name):
            self.format_string = format_string
            self.var_name = var_name

        def render(self, context):
            context[self.var_name] = datetime.datetime.now().strftime(self.format_string)
            return ""

    def do_current_time(parser, token):
        # This version uses a regular expression to parse tag contents.
        try:
            # Splitting by None == splitting by spaces.
            tag_name, arg = token.contents.split(None, 1)
        except ValueError:
            raise template.TemplateSyntaxError(
                "%r tag requires arguments" % token.contents.split()[0]
            )
        m = re.search(r"(.*?) as (\w+)", arg)
        if not m:
            raise template.TemplateSyntaxError("%r tag had invalid arguments" % tag_name)
        format_string, var_name = m.groups()
        if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):
            raise template.TemplateSyntaxError(
                "%r tag's argument should be in quotes" % tag_name
            )
        return CurrentTimeNode3(format_string[1:-1], var_name)

La differenza qui è che `do_current_time()` prende la stringa di formattazione ed il nome di variabile, passandole entrambe a `CurrentTimeNode3`.

Infine, se hai solo bisogno di avere una sintassi semplice per il tuo template tag personalizzato che aggiorna il context, considera l’utilizzo dello shortcut `simple_tag()`, che supporta l’assegnamento dei risultati del tag ad una variabile di template.

### Analisi fino ad un altro blocco tag

I tag di template possono funzionare in tandem. Per esempio, il tag standard `{% comment %}` nasconde ogni cosa fino a `{% endcomment %}`. Per creare un tag di template come questo, usa `parser.parse()` nella tua funzione di compilazione.

Ecco come potrebbe essere implementato un tag semplificato `{% comment %}`:

    def do_comment(parser, token):
        nodelist = parser.parse(("endcomment",))
        parser.delete_first_token()
        return CommentNode()

    class CommentNode(template.Node):
        def render(self, context):
            return ""

Nota

L’implementazione reale di `{% comment %}` è leggermente diversa nel fatto che permette a template tag rotti di apparire tra `{% comment %}` e``{% endcomment %}``. Lo fa chiamando `parser.skip_past('endcomment')` invece di `parser.parse(('endcomment',))` seguito da `parser.delete_first_token()`, evitando la generazione di una node list.

`parser.parse()` prende una tupla di nomi di tag di blocco «fino a quali fare l’analisi». Restituisce una istanza di `django.template.NodeList`, che è una lista di tutti gli oggetti `Node` che il parser ha incontrato «prima» di incontrare un qualsiasi tag presente nella lista.

In `"nodelist = parser.parse(('endcomment',))"` nell’esempio sopra, `nodelist` è una lista di nodi tra `{% comment %}` e `{% endcomment %}`, senza contare `{% comment %}` e``{% endcomment %}`` stessi.

Dopo che `parser.parse()` è stato chiamato, il parser non ha ancora «consumato» il tag `{% endcomment %}`, quindi il codice ha bisogno di chiamare splicitamente``parser.delete_first_token()``.

`CommentNode.render()` restituisce una stringa vuota. Ogni cosa tra `{% comment %}` e `{% endcomment %}` viene ignorata.

### Analizza fino ad un’altro tag, e salva i contenuti

Nell’esempio precedente, `do_comment()` ha scartato tutto quel che c’era tra `{% comment %}` e `{% endcomment %}`. Invece di fare così, è possibile fare qualcosa con il codice che si trova tra i tra i tag.

Per esempio, ecco un template tag personalizzato, `{% upper %}`, che rende maiuscolo tutto quel che c’è tra sè stesso e `{% endupper %}`.

Utilizzo:

    {% upper %}This will appear in uppercase, {{ your_name }}.{% endupper %}

Come nell’esempio precedente, useremo `parser.parse()`. Ma questa volta, passeremo la `nodelist` risultante a `Node`:

    def do_upper(parser, token):
        nodelist = parser.parse(("endupper",))
        parser.delete_first_token()
        return UpperNode(nodelist)

    class UpperNode(template.Node):
        def __init__(self, nodelist):
            self.nodelist = nodelist

        def render(self, context):
            output = self.nodelist.render(context)
            return output.upper()

L’unico concetto nuovo qui è `self.nodelist.render(context)` in `UpperNode.render()`.

For more examples of complex rendering, see the source code of `{% for %}` in django/template/defaulttags.py and `{% if %}` in django/template/smartif.py.