So wiederholen Sie einen „Block“ in einer Django-Vorlage

126

Ich möchte dasselbe {% block%} zweimal in derselben Django-Vorlage verwenden. Ich möchte, dass dieser Block mehr als einmal in meiner Basisvorlage angezeigt wird:

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

Und dann erweitern Sie es:

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

Ich werde eine Ausnahme bekommen, da Django möchte, dass der Block nur einmal erscheint:

TemplateSyntaxError at /

Das Tag 'block' mit dem Namen 'title' wird mehrmals angezeigt

Eine schnelle und schmutzige Lösung würde den Block sein Duplizieren Titel in title1 und title2 :

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

Dies ist jedoch ein Verstoß gegen das DRY- Prinzip. Es wäre sehr schwierig, da ich viele Vorlagen erbe und auch, weil ich nicht zur Hölle fahren will ;-)

Gibt es einen Trick oder eine Lösung für dieses Problem? Wie kann ich denselben Block in meiner Vorlage wiederholen, ohne den gesamten Code zu duplizieren?

David Arcos
quelle
1
Siehe auch die Lösung zu dieser Frage stackoverflow.com/q/1178743/168034
phunehehe
2
Siehe diese Antwort insbesondere auf die Frage phunehehe Links zu.
Tobu

Antworten:

69

Ich denke, dass die Verwendung des Kontextprozessors in diesem Fall ein Overkill ist. Sie können dies leicht tun:

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

und dann:

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

und so weiter ... Sieht aus wie DRY-kompatibel.

dqd
quelle
1
Ich könnte es morgen versuchen - ich habe mich gefragt, wie ich ein bisschen Wiederholung in den Vorlagen sparen kann, und dies scheint ein guter Ansatz zu sein. Vielen Dank.
Thebiglife
1
Dieser Ansatz ist ausgezeichnet. Ich habe meine base.html in base.html und superbase.html aufgeteilt, sodass dies auch funktioniert, wenn Sie ein Standardtitel-Markup (wie ein h1) in Ihre freigegebenen Vorlagen einfügen möchten. Seiten können den Inhalt des Schriftfelds weiterhin überschreiben und es wird an beiden Stellen aktualisiert.
SystemParadox
2
Dies erlaubt es nicht, den Text mehr als zweimal zu verwenden, oder?
Dennis Golomazov
1
Denis Golomazov: Nein. In diesem Fall ist es besser, das Makro-Plugin zu verwenden (siehe unten).
dqd
1
Kann auch umgekehrt angewendet werden: Definieren des h1Inhalts innerhalb des Blocks, der das definiert title. Oder ein Block, der einen Teil des definiert title.
Mikael Lindlöf
83

Verwenden Sie das Django-Vorlagenmakro-Plugin:

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

oder

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>

und

# 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>

und

# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}
John R Perry
quelle
2
Das ist fantastisch! Dies kann die Probleme, die ich beim Teilen von Vorlagen mit Django-Schleifen und Ajax-Datenschleifen habe, wirklich beseitigen.
Glycerin
1
Gute Lösung. Es ist jedoch "use_macro". "usemacro" ist falsch.
Ramtin
Sollte definitiv standardmäßig in Django eingebaut sein.
zepp.lee
19

Sie möchten wahrscheinlich keinen Block verwenden, sondern nur eine Variable:

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

Anschließend legen Sie den Titel im Kontext fest.

Aaron Maenpaa
quelle
17
Wahrscheinlich trocken. Sie möchten den Titel jedoch nicht in der Ansicht festlegen. aber in den vorlagen.
Lakshman Prasad
6
Titel sollten innerhalb der Vorlagen festgelegt werden und nicht vom Kontext bereitgestellt werden. Sie müssen über eine Möglichkeit verfügen, diese "Titel" -Variable zu definieren. Andernfalls ist dies keine gute Lösung.
Guillaume Esquevin
Das ist, was die Django-Admin-Vorlagen tun (für {{title}}), aber das Definieren des Titels beim Entfernen ist unpraktisch.
Tobu
13

Hier ist ein Weg, den ich entdeckt habe, als ich versucht habe, dasselbe selbst zu tun:

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

Erfordert leider eine zusätzliche Datei, erfordert jedoch nicht, dass Sie den Titel aus der Ansicht übergeben.

Roman Starkov
quelle
Am Ende habe ich mich für die {% macro%} -Lösung entschieden, für die keine neue Datei erforderlich ist. Insgesamt kann ich genau das ausdrücken, was ich ausdrücken möchte.
Roman Starkov
einfach und effizient. Im Gegensatz zur Antwort von @dqd müssen die Blöcke nicht verschachtelt sein. Dies ist beispielsweise für Facebook- und Tag-Tags sehr nützlich, die möglicherweise denselben Inhalt wie andere Kopfattribute haben. upvote!
Benzkji
2
Gute Antwort. Dies scheint noch trockener zu sein als die Antwort von @ dqd, da Sie das <h1> -Tag nicht in jeder Vorlage wiederholen müssen, die die Basis erbt. Dies könnte die akzeptierte Antwort sein.
Anupam
Ausgezeichnet! Ich habe dies in komplexeren Szenarien verwendet, in denen ich die Fußzeilenzeile einer Tabelle oben wiederholen wollte. Und die <tr>Reihe war ziemlich komplex.
Karam
12

Sie können {% include subtemplate.html %}mehr als einmal verwenden. Es ist nicht dasselbe wie Blöcke, aber es macht den Trick.

Javier
quelle
Dies hat das gleiche Problem. Die Basisvorlage weiß nicht, welche Untervorlage enthalten sein soll.
Van Gale
Bitte beachten Sie, dass dies includelangsamer ist als block. docs.djangoproject.com/de/1.10/topics/performance/…
Wtower
5

Hier gibt es einige Diskussionen: http://code.djangoproject.com/ticket/4529 Offensichtlich lehnt das Django-Kernteam dieses Ticket ab, weil es der Meinung ist, dass dies kein häufig verwendetes Szenario ist, aber ich bin anderer Meinung.

Der Wiederholungsblock ist eine einfache und saubere Implementierung: https://github.com/SmileyChris/django-repeatblock

Vorlagenmakros sind andere, der Autor erwähnte jedoch, dass sie nicht sorgfältig getestet wurden: http://www.djangosnippets.org/snippets/363/

Ich habe Repeatblock verwendet.

Robert Mao
quelle
4
Das ursprüngliche Django-Repeatblock-Repository scheint gelöscht worden zu sein. Die beste Gabelung scheint github.com/phretor/django-repeatblock zu sein ; Ich habe auch github.com/ydm/django-sameas gefunden , für das kein 'wontfix'-Django-Patch erforderlich ist.
Natevw
4

Als Update für alle, die darauf stoßen, habe ich das oben erwähnte Snippet in eine Vorlagen-Tag-Bibliothek (Django-Makros) umgewandelt, die die Makros leistungsfähiger macht und auch ein wiederholtes Blockmuster explizit implementiert : Django-Makros .

Nick
quelle
4

Hier ist eine leichte Lösung, die der obigen do_setund der do_getAntwort auf das Vorlagen-Tag ähnelt . Mit Django können Sie den gesamten Vorlagenkontext an ein Tag übergeben, mit dem Sie eine globale Variable definieren können.

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

Benutzerdefiniertes Tag (kam hier auf die Idee: https://stackoverflow.com/a/33564990/2747924 ):

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

Vergessen {% load %}Sie auch nicht, Ihre benutzerdefinierten Tags zu verwenden oder sie zur Liste der integrierten Vorlagenoptionen hinzuzufügen, damit Sie sie nicht in jede Vorlage laden müssen. Die einzige Einschränkung für diesen Ansatz besteht darin {% define %}, dass er innerhalb eines Block-Tags aufgerufen werden muss, da untergeordnete Vorlagen nur Block-Tags rendern, die mit den übergeordneten Tags übereinstimmen. Ich bin mir nicht sicher, ob es einen Weg gibt, das zu umgehen. Stellen Sie außerdem sicher, dass der defineAnruf eingeht, bevor Sie versuchen, ihn offensichtlich zu verwenden.

Manncito
quelle
3

Aufbauend auf Van Gales Vorschlag können Sie get- und set-Tags erstellen, indem Sie Ihrer Datei templatetags.py Folgendes hinzufügen:

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)

Setzen Sie dann Werte in einer Vorlage über {% set foo %}put data here{% endset %}und erhalten Sie sie über {% get foo %}in einer anderen.

Kieran Hervold
quelle
Ich denke, das ist die eleganteste Lösung von allen. Danke Kieran und Van Gale!
Robert Lacroix
Das ist ziemlich schick, aber es scheint noch besser zu sein, alle Knoten im Set-Tag zu rendern, sonst werden sie von Get immer wieder gerendert. Ich kann mir Gründe vorstellen, die eine gute Idee sein könnten (den gleichen gespeicherten Block in verschiedenen Blöcken auf einer Seite zu rendern), aber ich dachte nur, ich würde darauf hinweisen.
Acjay
3

Auch ich habe in meinen Vorlagendateien das gleiche Bedürfnis nach einem wiederholten {% block%} festgestellt. Das Problem ist, dass ich möchte, dass ein Django {% block%} in beiden Fällen einer Django-Bedingung verwendet wird, und dass der {% block%} von nachfolgenden Dateien überschrieben werden soll, die die aktuelle Datei möglicherweise erweitern. (In diesem Fall ist das, was ich möchte, definitiv eher ein Block als eine Variable, da ich sie technisch nicht wiederverwenden kann. Sie wird nur an beiden Enden einer Bedingung angezeigt.

Das Problem:

Der folgende Django-Vorlagencode führt zu einem Vorlagensyntaxfehler, aber ich denke, es ist ein gültiger "Wunsch", einen definierten {% block%} in einer Bedingung wiederzuverwenden (IE, warum überprüft der Django-Parser die Syntax an BEIDEN Enden? Sollte eine Bedingung nicht nur die WAHRHEITsbedingung validieren?)

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

Die Lösung:

Sie können ein {% include%} verwenden, um einen {% block%} mehr als einmal bedingt einzufügen. Dies hat bei mir funktioniert, da der Django-Syntaxprüfer nur die WAHRHEIT {% include%} enthält. Siehe das Ergebnis unten:

# 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 %}
Potench
quelle
2

Ich benutze diese Antwort , um die Dinge trocken zu halten.

{% extends "base.html" %}

{% with "Entry Title" as title %}
    {% block title %}{{ title }}{% endblock %}
    {% block h1 %}{{ title }}{% endblock %}
{% endwith %}
Christian Long
quelle
1

Hierfür gibt es zwei einfache Lösungen.

Am einfachsten ist es, Ihren Titel in eine Kontextvariable einzufügen. Sie würden die Kontextvariable in Ihrer Ansicht festlegen.

Wenn Sie so etwas wie generische Ansichten verwenden und keine views.py für Bilder, Katzen usw. haben, können Sie ein benutzerdefiniertes Vorlagen-Tag verwenden, das eine Variable im Kontext festlegt .

Wenn Sie diesen Weg gehen, können Sie Folgendes tun:

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

Dann in Ihrer base.html:

...
{% block title %}{{ page_title }}{% endblock %}
...
<h1>{{ page_title }}</h1>
Van Gale
quelle
JedochAny 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
0

Die ausgewählte Antwort spielt auf eine einfache Problemumgehung an, bei der ein Tag in ein anderes in der untergeordneten Vorlage eingeschlossen wird, um beiden den gleichen Wert zu geben. Ich benutze dies für solche sozialen Bilder.

Untergeordnete Vorlage:

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

Dann im Elternteil 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 %}">
...
Rich Ross
quelle
-3

In Zweig können Sie dies wie folgt machen:

# 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 %}
Mars
quelle
3
Dies ist eine Frage zu Django.
François Constant