Jak powtórzyć „blok” w szablonie django

126

Chcę użyć tego samego {% block%} dwa razy w tym samym szablonie django. Chcę, aby ten blok pojawiał się więcej niż raz w moim szablonie podstawowym:

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

A potem przedłużyć:

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

Dostanę wyjątek, ponieważ Django chce, aby blok pojawił się tylko raz:

TemplateSyntaxError w /

Znacznik „blok” z nazwą „tytuł” ​​pojawia się więcej niż raz

Szybki i brudny rozwiązaniem byłoby powielenie bloku tytuł do Title1 i Title2 :

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

Ale jest to naruszenie zasady DRY . Byłoby to bardzo trudne, ponieważ mam wiele dziedziczących szablonów, a także dlatego, że nie chcę iść do diabła ;-)

Czy jest jakaś sztuczka lub obejście tego problemu? Jak mogę powtórzyć ten sam blok w moim szablonie bez powielania całego kodu?

David Arcos
źródło
1
zobacz także rozwiązanie tego pytania stackoverflow.com/q/1178743/168034
phunehehe
2
Zobacz tę odpowiedź szczególnie na pytanie, do którego prowadzi linki.
Tobu

Odpowiedzi:

69

Myślę, że użycie procesora kontekstu jest w tym przypadku przesadą. Możesz to łatwo zrobić:

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

i wtedy:

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

i tak dalej ... Wygląda na kompatybilny z DRY.

dqd
źródło
1
Może spróbuję jutro - zastanawiałem się, jak zaoszczędzić trochę powtórzeń w szablonach i wydaje się, że to dobre podejście. dzięki.
thebiglife
1
To podejście jest doskonałe. Podzieliłem mój base.html na base.html i superbase.html, więc działa to również, jeśli chcesz umieścić standardowe znaczniki tytułu (takie jak h1) w udostępnianych szablonach. Strony mogą nadal zastępować zawartość tabelki rysunkowej i będzie ona aktualizowana w obu lokalizacjach.
SystemParadox
2
To nie pozwala na użycie tekstu więcej niż dwa razy, prawda?
Dennis Golomazov
1
Denis Golomazov: Nie. W takim przypadku lepiej jest skorzystać z wtyczki makro (patrz poniżej).
dqd
1
Można też zastosować na odwrót: definiując h1zawartość wewnątrz bloku, który definiuje title. Lub blokiem, który definiuje część z poniższych title.
Mikael Lindlöf
83

Użyj wtyczki makr szablonów Django:

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

lub

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>

i

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

i

# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}
John R. Perry
źródło
2
To jest fantastyczne! To może naprawdę rozwiązać problemy, które mam ze współdzieleniem szablonów za pomocą pętli django i pętli danych Ajax.
gliceryna
1
Dobre rozwiązanie. Jednak jest to „use_macro”. „usemacro” jest błędne.
Ramtin
Zdecydowanie powinien być domyślnie wbudowany w Django.
zepp.lee
19

Prawdopodobnie nie chcesz używać bloku, ale raczej po prostu użyć zmiennej:

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

Następnie ustawia się tytuł poprzez kontekst.

Aaron Maenpaa
źródło
17
Prawdopodobnie suchy. Ale nie chciałbyś ustawiać tytułu w widoku; ale w szablonach.
Lakshman Prasad
6
Tytuły należy ustawiać z poziomu szablonów, a nie z kontekstu, trzeba mieć sposób na zdefiniowanie tej zmiennej „tytuł”, w przeciwnym razie nie jest to dobre rozwiązanie.
Guillaume Esquevin,
To właśnie robią szablony administracyjne django (dla {{title}}), ale definiowanie tytułu przy usuwaniu jest niewygodne.
Tobu
13

Oto sposób, który odkryłem, próbując zrobić to samo:

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

Wymaga niestety dodatkowego pliku, ale nie wymaga podania tytułu z widoku.

Roman Starkov
źródło
Ostatecznie zdecydowałem się na rozwiązanie {% macro%}, które nie wymaga nowego pliku i ogólnie pozwala mi wyrazić dokładnie to, co chcę wyrazić.
Roman Starkov
proste i wydajne. w przeciwieństwie do odpowiedzi @dqd s, bloki nie muszą być zagnieżdżane, bardzo przydatne na przykład dla tagów facebook og, które mogą mieć taką samą zawartość jak inne atrybuty head. głosuj!
benzkji
2
Świetna odpowiedź. Wydaje się to nawet bardziej SUCHE niż odpowiedź @ dqd, ponieważ nie musisz powtarzać znacznika <h1> w każdym szablonie, który dziedziczy base. To mogłaby być akceptowana odpowiedź.
Anupam,
Doskonały! Używałem tego w bardziej złożonych scenariuszach, w których chciałem powtórzyć wiersz stopki tabeli na górze. A <tr>kłótnia była dość złożona.
caram
12

możesz użyć {% include subtemplate.html %}więcej niż raz. to nie to samo, co bloki, ale załatwia sprawę.

Javier
źródło
To ma ten sam problem. Szablon podstawowy nie będzie wiedział, który szablon podrzędny uwzględnić.
Van Gale
Należy pamiętać, że includejest wolniejszy niż block. docs.djangoproject.com/en/1.10/topics/performance/…
Wtower,
5

Tutaj jest dyskusja: http://code.djangoproject.com/ticket/4529 Oczywiście główny zespół django odrzuca to zgłoszenie, ponieważ uważają, że nie jest to często używany scenariusz, jednak się z tym nie zgadzam.

Powtarzaj blok to prosta i czysta implementacja: https://github.com/SmileyChris/django-repeatblock

makra szablonów to kolejne, jednak autor wspomniał, że nie zostały one dokładnie przetestowane: http://www.djangosnippets.org/snippets/363/

Użyłem powtarzającego się bloku.

Robert Mao
źródło
4
Wydaje się, że oryginalne repozytorium django-repeatblock zostało usunięte. Najlepszym widelcem wydaje się być github.com/phretor/django-repeatblock ; Znalazłem również github.com/ydm/django-sameas , który nie wymaga łatki Django „wontfix”.
natevw
4

Jako aktualizacja dla każdego, kto zetknie się z tym, wziąłem wspomniany powyżej fragment i przekształciłem go w bibliotekę tagów szablonów, django-macros, dzięki czemu makra są potężniejsze, a także jawnie implementuje powtarzający się wzorzec blokowy: django-macros .

Nacięcie
źródło
4

Oto lekkie rozwiązanie podobne do powyższego do_seti do_getodpowiedź tagu szablonu. Django umożliwia przekazanie całego kontekstu szablonu do znacznika, który umożliwia zdefiniowanie zmiennej globalnej.

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 niestandardowy (pomysł tutaj: https://stackoverflow.com/a/33564990/2747924 ):

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

Nie zapomnij również o {% load %}swoich niestandardowych tagach lub dodaj je do listy wbudowanych opcji szablonów, aby nie trzeba było ich ładować w każdym szablonie. Jedynym ograniczeniem tego podejścia jest to, że {% define %}musi być wywoływane z poziomu tagu bloku, ponieważ szablony potomne renderują tylko tagi bloków, które pasują do tagów nadrzędnych. Nie jestem pewien, czy jest na to sposób. Upewnij się również, że definepołączenie nadeszło, zanim spróbujesz go oczywiście użyć.

manncito
źródło
3

Opierając się na sugestii Van Gale'a, możesz utworzyć tagi pobierania i ustawiania, dodając następujące elementy do pliku 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)

Następnie ustaw wartości w jednym szablonie za pośrednictwem {% set foo %}put data here{% endset %}i pobierz je {% get foo %}w innym.

kieran hervold
źródło
Myślę, że to najbardziej eleganckie rozwiązanie ze wszystkich. Dzięki Kieran i Van Gale!
Robert Lacroix,
To całkiem sprytne, ale wydaje się, że jeszcze lepiej byłoby wyrenderować wszystkie węzły w tagu Set, w przeciwnym razie są one renderowane w kółko przez Get. Przychodzi mi do głowy powody, które mogą być dobrym pomysłem (renderowanie tego samego przechowywanego bloku w różnych blokach na stronie), ale pomyślałem, że wskazałbym to.
acjay
3

Ja także natknąłem się na tę samą potrzebę powtórzenia {% block%} w moich plikach szablonów. Problem polega na tym, że chcę, aby Django {% block%} był używany w każdym przypadku warunku Django i chcę, aby {% block%} był nadpisywalny przez kolejne pliki, które mogą rozszerzyć bieżący plik. (Więc w tym przypadku to, czego chcę, to zdecydowanie bardziej blok niż zmienna, ponieważ technicznie nie używam jej ponownie, po prostu pojawia się na każdym końcu warunku.

Problem:

Poniższy kod szablonu Django spowoduje błąd składni szablonu, ale myślę, że jest to poprawne "pragnienie" ponownego użycia zdefiniowanego {% block%} warunkowego (IE, dlaczego składnia sprawdzania poprawności parsera Django jest na OBU końcach warunkowej, czy nie powinien on jedynie potwierdzać warunku PRAWDA?)

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

Rozwiązanie:

Możesz użyć {% include%}, aby warunkowo wstawić {% block%} więcej niż raz. To zadziałało dla mnie, ponieważ moduł sprawdzania składni Django zawiera tylko PRAWDA {% zawiera%}. Zobacz wynik poniżej:

# 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
źródło
2

Używam tej odpowiedzi, aby wszystko było suche.

{% extends "base.html" %}

{% with "Entry Title" as title %}
    {% block title %}{{ title }}{% endblock %}
    {% block h1 %}{{ title }}{% endblock %}
{% endwith %}
Christian Long
źródło
1

Są na to dwa proste rozwiązania.

Najłatwiej jest umieścić tytuł w zmiennej kontekstowej. Możesz ustawić zmienną kontekstową w swoim widoku.

Jeśli używasz czegoś takiego jak widoki ogólne i nie masz pliku views.py dla zdjęć, kotów itp., Możesz przejść drogą niestandardowego tagu szablonu, który ustawia zmienną w kontekście .

Wybranie tej trasy pozwoliłoby ci zrobić coś takiego:

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

Następnie w swoim base.html:

...
{% block title %}{{ page_title }}{% endblock %}
...
<h1>{{ page_title }}</h1>
Van Gale
źródło
JednakAny 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

Wybrana odpowiedź nawiązuje do łatwego obejścia polegającego na umieszczeniu jednego tagu wewnątrz drugiego w szablonie podrzędnym, aby nadać im tę samą wartość. Używam tego do takich zdjęć społecznościowych.

Szablon podrzędny:

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

Następnie u rodzica 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
źródło
-3

W gałązce możesz zrobić to tak:

# 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
źródło
3
To jest pytanie o Django.
François Constant