Come ripetere un "blocco" in un modello django


126

Voglio usare lo stesso {% block%} due volte nello stesso modello di django. Voglio che questo blocco appaia più di una volta nel mio modello di base:

# base.html
<html>
    <head>
        <title>{% block title %}My Cool Website{% endblock %}</title>
    </head>
    <body>
        <h1>{% block title %}My Cool Website{% endblock %}</h1>
    </body>
</html>

E poi estenderlo:

# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}

# pictures.html
{% extends 'base.html' %}
{% block title %}My Pictures{% endblock %}

# cats.html
{% extends 'base.html' %}
{% block title %}My Cats{% endblock %}

Otterrò un'eccezione, poiché Django vuole che il blocco appaia una sola volta:

TemplateSyntaxError su /

Il tag "block" con il nome "title" appare più di una volta

Una soluzione rapida e sporca sarebbe la duplicazione del titolo del blocco in titolo1 e titolo2 :

# blog.html
{% extends 'base.html' %}
{% block title1 %}My Blog{% endblock %}
{% block title2 %}My Blog{% endblock %}

Ma questa è una violazione del principio DRY . Sarebbe molto difficile dato che ho molti modelli ereditari, e anche perché non voglio andare all'inferno ;-)

C'è qualche trucco o soluzione per questo problema? Come posso ripetere lo stesso blocco nel mio modello, senza duplicare tutto il codice?


1
anche vedere la soluzione su questa questione stackoverflow.com/q/1178743/168034
phunehehe

2
Vedi questa risposta in particolare alla domanda a cui si collega phunehehe.
Tobu,

Risposte:


69

Penso che l'uso del processore di contesto sia in questo caso eccessivo. Puoi farlo facilmente:

#base.html
<html>
    <head>
        <title>{% block title %}My Cool Website{% endblock %}</title>
    </head>
    <body>
        {% block content %}{% endblock %}
    </body>
</html>

e poi:

# blog.html
{% extends 'base.html' %}
{% block content %}
    <h1>{% block title %}My Blog{% endblock %}</h1>
    Lorem ipsum here...
{% endblock %}

e così via ... Sembra compatibile DRY.


1
Potrei provare questo domani - mi chiedevo come salvare un po 'di ripetizione nei template e questo sembra un buon approccio. Grazie.
thebiglife,

1
Questo approccio è eccellente. Ho diviso il mio base.html in base.html e superbase.html, quindi funziona anche se si desidera inserire un markup del titolo standard (come un h1) nei modelli condivisi. Le pagine possono ancora sovrascrivere il contenuto del cartiglio e verrà aggiornato in entrambe le posizioni.
SystemParadox il

2
Questo non consente di utilizzare il testo più di due volte, vero?
Dennis Golomazov,

1
Denis Golomazov: No. In tal caso, è meglio usare il plugin macro (vedi sotto).
dqd

1
Può anche essere applicato al contrario: definizione del h1contenuto all'interno del blocco che definisce il file title. O un blocco che definisce una parte di title.
Mikael Lindlöf

83

Usa il plug-in per macro modello Django:

https://gist.github.com/1715202 (django> = 1.4)

o

http://www.djangosnippets.org/snippets/363/ (django <1.4)

django> = 1.4

# base.html
{% kwacro title %}
    {% block title %}My Cool Website{% endblock %}
{% endkwacro %}

<html>
    <head>
        <title>{% usekwacro title %}</title>
    </head>
    <body>
        <h1>{% usekwacro title %}</h1>
    </body>
</html>

e

# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}

django <1.4

# base.html
{% macro title %}
    {% block title %}My Cool Website{% endblock %}
{% endmacro %}

<html>
    <head>
        <title>{% usemacro title %}</title>
    </head>
    <body>
        <h1>{% usemacro title %}</h1>
    </body>
</html>

e

# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}

2
È fantastico! Questo può davvero ripulire i problemi che ottengo con la condivisione di modelli con loop django e loop dati ajax.
Glicerina

1
Buona soluzione Tuttavia, è "use_macro". "usemacro" è sbagliato.
Ramtin,

Deve sicuramente essere integrato in Django per impostazione predefinita.
zepp.lee,

19

Probabilmente non vuoi effettivamente usare un blocco ma piuttosto usare solo una variabile:

# base.html
<html>
    <head>
        <title>{{ title|default:"My Cool Website" }}</title>
    </head>
    <body>
        <h1>{{ title|default:"My Cool Website" }}</h1>
    </body>
</html>

Quindi impostare il titolo attraverso il contesto.


17
Probabilmente asciutto. Ma non vorrai impostare il titolo all'interno della vista; ma nei modelli.
Lakshman Prasad,

6
I titoli devono essere impostati all'interno dei modelli, non forniti dal contesto, è necessario disporre di un modo per definire questa variabile "title", altrimenti questa non è una buona soluzione.
Guillaume Esquevin,

Questo è ciò che fanno i modelli di amministrazione di django (per {{title}}), ma definire il titolo alla rimozione è scomodo.
Tobu,

13

Ecco un modo che ho scoperto quando ho provato a fare la stessa cosa da solo:

# base_helper.html
<html>
    <head>
        <title>{% block _title1 %}{% endblock %}</title>
    </head>
    <body>
        <h1>{% block _title2 %}{% endblock %}</h1>
    </body>
</html>


# base.html
{% extends "base_helper.html" %}

# Copy title into _title1 & _title2, using "My Cool Website" as a default.
{% block _title1 %}{% block _title2 %}{% block title %}My Cool Website{% endblock %}{% endblock %}{% endblock %}

Sfortunatamente richiede un file aggiuntivo, ma non è necessario passare il titolo dalla vista.


Alla fine ho optato per la soluzione {% macro%}, che non richiede un nuovo file, e nel complesso mi permette di esprimere esattamente quello che voglio esprimere.
Roman Starkov,

semplice ed efficiente. a differenza della risposta di @dqd, i blocchi non devono essere nidificati, molto utile ad esempio per i tag facebook og, che potrebbe avere lo stesso contenuto di altri attributi head. upvote!
benzkji,

2
Bella risposta. Questo sembra persino ASCIUTTO della risposta di @ dqd poiché non è necessario ripetere il tag <h1> in ogni modello che eredita la base. Questa potrebbe essere la risposta accettata.
Anupam,

Eccellente! L'ho usato in scenari più complessi in cui volevo ripetere la riga a piè di pagina di una tabella all'inizio. E la <tr>fila era piuttosto complessa.
caram


5

Ci sono alcune discussioni qui: http://code.djangoproject.com/ticket/4529 Ovviamente il core team di django rifiuta questo ticket perché pensa che questo non sia uno scenario usato comunemente, tuttavia non sono d'accordo.

Repeat Block è un'implementazione semplice e chiara per questo: https://github.com/SmileyChris/django-repeatblock

le macro modello sono un'altra, tuttavia l'autore ha affermato che non è stato testato con cura: http://www.djangosnippets.org/snippets/363/

Ho usato repeatblock.


4
Il repository django-repeatblock originale sembra essere stato cancellato. Il miglior fork sembra essere github.com/phretor/django-repeatblock ; Ho anche trovato github.com/ydm/django-sameas , che non richiede una patch Django "wontfix".
natevw,

4

Come aggiornamento per chiunque si imbatta in questo, ho preso lo snippet sopra menzionato e l'ho trasformato in una libreria di tag modello, django-macros, rende le macro più potenti e implementa esplicitamente uno schema a blocchi ripetuto: django-macros .


4

Ecco una soluzione leggera simile alla precedente do_sete alla do_getrisposta del tag modello. Django ti consente di passare l'intero contesto del modello in un tag che ti consente di definire una variabile globale.

base.html:

<!DOCTYPE html>
<html lang="en">
<head>
  {% block head %}
    <title>{{ title }}</title>
  {% endblock %}
</head>
<body>
  <h1>{{ title }}</h1>
</body>
</html>

page.html:

{% extends "base.html" %}

{% block head %}
  {% define 'title' 'Homepage | title' %}
  {{ block.super }}
{% endblock %}

tag personalizzato (l'idea qui: https://stackoverflow.com/a/33564990/2747924 ):

@register.simple_tag(takes_context=True)
def define(context, key, value):
    context.dicts[0][key] = value
    return ''

Inoltre, non dimenticare {% load %}i tag personalizzati o aggiungerli all'elenco integrato delle opzioni del modello in modo da non doverli caricare in ogni modello. L'unica limitazione a questo approccio è la {% define %}necessità di essere richiamato all'interno di un tag di blocco poiché i modelli figlio visualizzano solo tag di blocco che corrispondono ai tag principali. Non sono sicuro che ci sia un modo per aggirare questo. Assicurati anche che la definechiamata arrivi prima di provare a usarla ovviamente.


3

Sulla base del suggerimento di Van Gale, è possibile creare tag get e set aggiungendo quanto segue al file templatetags.py:

register = template.Library()

Stateful = {}
def do_set(parser, token):
    _, key = token.split_contents()
    nodelist = parser.parse(('endset',))
    parser.delete_first_token()  # from the example -- why?
    return SetStatefulNode(key,nodelist)

class SetStatefulNode(template.Node):
    def __init__(self, key, nodes):
        Stateful[key] = nodes
    def render(self, context):
        return ''
register.tag('set', do_set)

def do_get(parser, token):
    tag_name, key = token.split_contents()
    return GetStatefulNode(key)

class GetStatefulNode(template.Node):
    def __init__(self, key):
       self.key = key
    def render(self, context):
        return ''.join( [x.render(context) for x in Stateful[self.key]] )

register.tag('get', do_get)

Quindi impostare i valori in un modello tramite {% set foo %}put data here{% endset %}e ottenerli tramite {% get foo %}in un altro.


Penso che sia la soluzione più elegante di tutte. Grazie Kieran e Van Gale!
Robert Lacroix,

È piuttosto fluido, ma sembra che potrebbe essere ancora meglio rendere tutti i nodi nel tag Set, altrimenti vengono resi più e più volte da Get. Riesco a pensare a motivi che potrebbero essere una buona idea (rendering dello stesso blocco memorizzato all'interno di blocchi diversi in una pagina), ma ho pensato di segnalarlo.
Acjay,

3

Anch'io ho riscontrato la stessa necessità di ripetere {% block%} nei miei file modello. Il problema è che voglio un Django {% block%} da utilizzare in entrambi i casi di un condizionale Django e desidero che il {% block%} sia sovrascrivibile dai file successivi che potrebbero estendere il file corrente. (Quindi in questo caso, quello che voglio è sicuramente più un blocco che una variabile perché non lo sto riutilizzando tecnicamente, appare solo su entrambe le estremità di un condizionale.

Il problema:

Il seguente codice modello Django genererà un errore di sintassi del modello, ma penso che sia un "desiderio" valido avere un {% block%} definito riutilizzato in un condizionale (IE, perché il parser Django convalida la sintassi su ENTRAMBE le estremità di un condizionale, non dovrebbe solo validare la condizione di VERITÀ?)

# This example shows a {{ DEBUG }} conditional that loads 
#   Uncompressed JavaScript files if TRUE 
#   and loads Asynchronous minified JavaScript files if FALSE.  

# BASE.html
{% if DEBUG %}
    <script src="{{MEDIA_URL}}js/flatfile.1.js"></script>
    <script src="{{MEDIA_URL}}js/flatfile.2.js"></script>
    <script src="{{MEDIA_URL}}js/flatfile.3.js"></script>
    <script type="text/javascript">
        {% block page_js %}
            var page = new $site.Page();
        {% endblock page_js %}
    </script>
{% else %}
    <script type="text/javascript">
        // load in the PRODUCTION VERSION of the site
        // minified and asynchronosly loaded
        yepnope([
            {
                load : '{MEDIA_URL}}js/flatfiles.min.js',
                wait : true,
                complete : function() {
                    {% block page_js %} // NOTE THE PAGE_JS BLOCK
                        var page = new $site.Page();
                    {% endblock page_js %}
                }
            }
        )];
    </script>
{% endif %}

# ABOUT.html
{% extends 'pages/base.html' %}
{% block page_js %}
var page = new $site.Page.About();
{% endblock page_js %}

La soluzione:

Puoi utilizzare un {% include%} per inserire condizionalmente un {% block%} più di una volta. Questo ha funzionato per me perché il controllo della sintassi di Django include solo il TRUTHY {% include%}. Vedi il risultato qui sotto:

# partials/page.js
{% block page_js %}
    var page = new $site.Page();    
{% endblock %}

# base.html
{% if DEBUG %}
    <script src="{{MEDIA_URL}}js/flatfile.1.js"></script>
    <script src="{{MEDIA_URL}}js/flatfile.2.js"></script>
    <script src="{{MEDIA_URL}}js/flatfile.3.js"></script>
    <script type="text/javascript">
        {% include 'partials/page_js.html' %}
    </script>
{% else %}
    <script type="text/javascript">
        yepnope([
            {
                load : '{MEDIA_URL}}js/flatfiles.min.js',
                wait : true,
                complete : function() {
                    {% include 'partials/page_js.html' %}
                }
            }
        )];
    </script>
{% endif %}

2

Uso questa risposta per mantenere le cose asciutte.

{% extends "base.html" %}

{% with "Entry Title" as title %}
    {% block title %}{{ title }}{% endblock %}
    {% block h1 %}{{ title }}{% endblock %}
{% endwith %}

1

Ci sono due soluzioni facili per questo.

Il più semplice è inserire il tuo titolo in una variabile di contesto. Impostare la variabile di contesto nella vista.

Se stai usando qualcosa come le viste generiche e non hai un views.py per immagini, gatti, ecc. Allora puoi seguire la strada di un tag modello personalizzato che imposta una variabile nel contesto .

Seguire questa strada ti permetterebbe di fare qualcosa del tipo:

{% extends "base.html" %}
{% load set_page_title %}
{% page_title "My Pictures" %}
...

Quindi nel tuo base.html:

...
{% block title %}{{ page_title }}{% endblock %}
...
<h1>{{ page_title }}</h1>

TuttaviaAny variable set in the context will only be available in the same block of the template in which it was assigned. This behavior is intentional; it provides a scope for variables so that they don’t conflict with context in other blocks.
Jonathan il

0

La risposta selezionata allude a una soluzione semplice per avvolgere un tag all'interno di un altro nel modello figlio per dare ad entrambi lo stesso valore. Lo uso per le immagini social in questo modo.

Modello figlio:

{% extends 'base.html' %}
...
{% block meta_image %}
{% block meta_image_secure %}
{% if object.cover_pic %}
{{ object.cover_pic.url }}
{% else %}
https://live-static.welovemicro.com/static/img/device-dark.png
{% endif %}
{% endblock %}
{% endblock %}
...

Quindi nel genitore base.html:

...
<meta property="og:image" itemprop="image" content="{% block meta_image %}https://live-static.welovemicro.com/static/img/device-dark.png{% endblock %}">
<meta property="og:image:secure_url" itemprop="image" content="{% block meta_image_secure %}https://live-static.welovemicro.com/static/img/device-dark.png{% endblock %}">
...

-3

In un ramoscello puoi renderlo come:

# base.html
<html>
    <head>
        <title>{{ block('title') }}</title>
    </head>
    <body>
        <h1>{{ block('title') }}</h1>
    </body>
</html>

# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}

# pictures.html
{% extends 'base.html' %}
{% block title %}My Pictures{% endblock %}

# cats.html
{% extends 'base.html' %}
{% block title %}My Cats{% endblock %}

3
Questa è una domanda su Django.
François Constant,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.