Bagaimana cara mengulang "blok" dalam templat Django

126

Saya ingin menggunakan {% block%} yang sama dua kali dalam template Django yang sama. Saya ingin blok ini muncul lebih dari satu kali di templat dasar saya:

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

Dan kemudian memperluasnya:

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

Saya akan mendapatkan pengecualian, karena Django ingin blok hanya muncul sekali:

TemplateSyntaxError di /

tag 'blokir' dengan nama 'judul' muncul lebih dari satu kali

Solusi cepat dan kotor akan menduplikasi judul blok ke title1 dan title2 :

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

Tapi ini merupakan pelanggaran prinsip KERING . Ini akan sangat sulit karena saya memiliki banyak template yang mewarisi, dan juga karena saya tidak ingin pergi ke neraka ;-)

Apakah ada trik atau penyelesaian masalah ini? Bagaimana saya bisa mengulangi blok yang sama di template saya, tanpa menduplikasi semua kode?

David Arcos
sumber
1
juga lihat solusi pada pertanyaan ini stackoverflow.com/q/1178743/168034
phunehehe
2
Lihat jawaban ini terutama untuk pertanyaan yang dia tautkan.
Tobu

Jawaban:

69

Saya pikir bahwa penggunaan pengolah konteks dalam hal ini adalah pembunuhan yang berlebihan. Anda dapat dengan mudah melakukan ini:

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

lalu:

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

dan seterusnya ... Sepertinya kompatibel KERING.

dqd
sumber
1
Saya mungkin akan mencoba ini besok - Saya sudah bertanya-tanya bagaimana cara menyimpan sedikit pengulangan dalam template dan ini sepertinya pendekatan yang bagus. Terima kasih.
thebiglife
1
Pendekatan ini sangat bagus. Saya telah membagi base.html menjadi base.html dan superbase.html, jadi ini juga berfungsi jika Anda ingin meletakkan markup judul standar (seperti h1) di templat bersama Anda. Halaman masih dapat menimpa konten blok judul dan akan diperbarui di kedua lokasi.
SystemParadox
2
Ini tidak memungkinkan untuk menggunakan teks lebih dari dua kali, bukan?
Dennis Golomazov
1
Denis Golomazov: Tidak. Dalam hal ini, lebih baik menggunakan plugin makro (lihat di bawah).
dqd
1
Dapat juga diterapkan sebaliknya: mendefinisikan h1konten di dalam blok yang mendefinisikan title. Atau blok yang mendefinisikan bagian dari title.
Mikael Lindlöf
83

Gunakan plugin macro template Django:

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

atau

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>

dan

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

dan

# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}
John R Perry
sumber
2
Ini fantastis! Ini benar-benar dapat membersihkan masalah yang saya dapatkan dengan berbagi template dengan django loops dan ajax data loops.
Glycerine
1
Solusi yang bagus Namun, itu adalah "use_macro". "usemacro" salah.
Ramtin
Pasti harus dibangun ke Django secara default.
zepp.keluar
19

Anda mungkin tidak benar-benar ingin menggunakan blok melainkan hanya menggunakan variabel:

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

Anda kemudian mengatur judul melalui konteksnya.

Aaron Maenpaa
sumber
17
Mungkin kering Tetapi Anda tidak ingin mengatur judul dalam tampilan; tetapi dalam template.
Lakshman Prasad
6
Judul harus ditetapkan dari dalam templat, tidak disediakan oleh konteks, Anda harus memiliki cara untuk mendefinisikan variabel "judul" ini, jika tidak ini bukan solusi yang baik.
Guillaume Esquevin
Itulah yang dilakukan templat admin django (untuk {{title}}), tetapi mendefinisikan judul pada penghapusan tidak nyaman.
Tobu
13

Inilah cara yang saya temukan ketika mencoba melakukan hal yang sama sendiri:

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

Sayangnya, membutuhkan file tambahan, tetapi tidak mengharuskan Anda meneruskan judul dari tampilan.

Roman Starkov
sumber
Pada akhirnya saya memilih solusi {% macro%}, yang tidak memerlukan file baru, dan secara keseluruhan memungkinkan saya mengekspresikan apa yang ingin saya ungkapkan.
Roman Starkov
sederhana dan efisien. tidak seperti jawaban @dqd, blok tidak perlu disarangkan, sangat berguna misalnya untuk tag og facebook, yang mungkin memiliki konten yang sama dengan atribut head lainnya. menang!
benzkji
2
Jawaban yang bagus Ini tampaknya lebih KERING daripada jawaban @ dqd karena Anda tidak perlu mengulangi tag <h1> di setiap templat yang mewarisi basis. Ini bisa menjadi jawaban yang diterima.
Anupam
Luar biasa! Saya telah menggunakan ini dalam skenario yang lebih kompleks di mana saya ingin mengulangi baris footer dari tabel di bagian atas. Dan <tr>barisannya agak rumit.
caram
12

Anda bisa menggunakan {% include subtemplate.html %}lebih dari satu kali. itu tidak sama dengan balok, tetapi melakukan trik.

Javier
sumber
Ini memiliki masalah yang sama. Templat dasar tidak akan tahu subtemplate mana yang akan disertakan.
Van Gale
Harap dicatat bahwa includeini lebih lambat daripada block. docs.djangoproject.com/en/1.10/topics/performance/…
Wtower
5

Ada beberapa diskusi di sini: http://code.djangoproject.com/ticket/4529 Jelas tim inti Django menolak tiket ini karena mereka pikir ini bukan skenario yang umum digunakan, namun saya tidak setuju.

blok berulang adalah implementasi sederhana dan bersih untuk ini: https://github.com/SmileyChris/django-repeatblock

makro template adalah satu lagi, namun penulis menyebutkan itu tidak diuji dengan hati-hati: http://www.djangosnippets.org/snippets/363/

Saya menggunakan repeatblock.

Robert Mao
sumber
4
Repositori django-repeatblock asli tampaknya telah dihapus. Garpu terbaik dari itu tampaknya adalah github.com/phretor/django-repeatblock ; Saya juga menemukan github.com/ydm/django-sameas , yang tidak memerlukan tambalan Django 'wontfix'.
natevw
4

Sebagai pembaruan bagi siapa pun yang menemukan ini, saya telah mengambil cuplikan yang disebutkan di atas dan mengubahnya menjadi pustaka tag templat, Django-macro, membuat makro lebih kuat dan juga menerapkan pola blok berulang secara eksplisit: Django-makro .

Nick
sumber
4

Berikut ini adalah solusi ringan mirip dengan jawaban tag templat di atas do_setdan do_get. Django memungkinkan Anda untuk melewatkan seluruh konteks templat ke dalam sebuah tag yang memungkinkan Anda untuk mendefinisikan variabel global.

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 khusus (ada idenya di sini: https://stackoverflow.com/a/33564990/2747924 ):

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

Juga jangan lupa pada {% load %}tag khusus Anda atau tambahkan ke daftar opsi bawaan templat sehingga Anda tidak perlu memuatnya di setiap templat. Satu-satunya batasan untuk pendekatan ini adalah {% define %}harus dipanggil dari dalam tag blok karena template anak hanya membuat tag blok yang cocok dengan tag induk. Tidak yakin apakah ada jalan keluarnya. Pastikan juga definepanggilan datang sebelum Anda mencoba menggunakannya dengan jelas.

manncito
sumber
3

Berdasarkan saran Van Gale, Anda bisa membuat get and set tag dengan menambahkan yang berikut ke file templatetags.py Anda:

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)

Kemudian atur nilai dalam satu templat melalui {% set foo %}put data here{% endset %}dan tempel templat-templat {% get foo %}lain.

kieran di sini
sumber
Saya pikir itu solusi paling elegan dari semuanya. Terima kasih Kieran dan Van Gale!
Robert Lacroix
Itu lumayan apik, tapi sepertinya akan lebih baik untuk merender semua node di tag Set, kalau tidak mereka akan di-render berulang-ulang oleh Get. Saya dapat memikirkan alasan yang mungkin merupakan ide yang bagus (merender blok yang disimpan sama di dalam blok yang berbeda pada satu halaman), tetapi saya hanya berpikir saya akan menunjukkannya.
acjay
3

Saya juga menemukan kebutuhan yang sama untuk pengulangan {% block%} di file templat saya. Masalahnya adalah bahwa saya ingin Django {% block%} untuk digunakan dalam kedua kasus kondisional Django, dan saya ingin {% block%} menjadi terlalu bisa ditulis oleh file-file berikutnya yang dapat memperpanjang file saat ini. (Jadi dalam hal ini, apa yang saya inginkan jelas lebih merupakan sebuah blok daripada variabel karena saya tidak secara teknis menggunakannya kembali, itu hanya muncul di kedua ujung kondisi.

Masalah:

Kode template Django berikut akan menghasilkan Kesalahan Sintaksis Templat, tetapi saya pikir itu adalah "keinginan" yang sah untuk memiliki {% block%} yang digunakan kembali dalam kondisi (IE, mengapa parser Django memvalidasi sintaks pada KEDUA ujung kondisional, bukankah itu hanya memvalidasi kondisi KEBENARAN?)

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

Solusinya:

Anda dapat menggunakan {% include%} untuk memasukkan {% block%} lebih dari satu syarat. Ini bekerja untuk saya karena pemeriksa sintaksis Django hanya menyertakan TRUTHY {% include%}. Lihat hasilnya di bawah ini:

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

Saya menggunakan jawaban ini untuk menjaga semuanya tetap kering.

{% extends "base.html" %}

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

Ada dua solusi mudah untuk ini.

Cara termudah adalah menempatkan judul Anda ke dalam variabel konteks. Anda akan mengatur variabel konteks dalam tampilan Anda.

Jika Anda menggunakan sesuatu seperti tampilan umum dan tidak memiliki views.py untuk gambar, kucing, dll. Maka Anda dapat menggunakan tag templat kustom yang menetapkan variabel dalam konteks .

Melewati rute ini akan memungkinkan Anda melakukan sesuatu seperti:

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

Kemudian di base.html Anda:

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

Jawaban yang dipilih menyinggung solusi yang mudah untuk membungkus satu tag di dalam yang lain dalam template anak untuk memberikan keduanya nilai yang sama. Saya menggunakan ini untuk gambar sosial seperti itu.

Template anak:

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

Kemudian di induk 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
sumber
-3

Dalam ranting Anda dapat membuat ini seperti:

# 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
sumber
3
Ini pertanyaan tentang Django.
François Constant