วิธีทำ "บล็อก" ซ้ำในเทมเพลต django


126

ฉันต้องการใช้{% block%} เดียวกันสองครั้งในเทมเพลต django เดียวกัน ฉันต้องการให้บล็อกนี้ปรากฏมากกว่าหนึ่งครั้งในเทมเพลตพื้นฐานของฉัน:

# base.html
<html>
    <head>
        <title>{% block title %}My Cool Website{% endblock %}</title>
    </head>
    <body>
        <h1>{% block title %}My Cool Website{% endblock %}</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 %}

ฉันจะได้รับข้อยกเว้นเนื่องจาก Django ต้องการให้บล็อกปรากฏเพียงครั้งเดียว:

TemplateSyntaxError ที่ /

แท็ก "block" ที่มีชื่อ "title" ปรากฏขึ้นมากกว่าหนึ่งครั้ง

วิธีการแก้ปัญหาที่รวดเร็วและสกปรกจะทำซ้ำบล็อกชื่อเข้าTITLE1และTITLE2 :

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

แต่นี่เป็นการละเมิดหลักการDRY มันจะยากมากเพราะฉันมีเทมเพลตที่สืบทอดมามากมายและเพราะฉันไม่อยากตกนรก ;-)

มีเคล็ดลับหรือวิธีแก้ไขปัญหานี้หรือไม่? ฉันจะทำซ้ำบล็อกเดียวกันในเทมเพลตโดยไม่ต้องทำซ้ำรหัสทั้งหมดได้อย่างไร


1
ดูวิธีแก้ปัญหาในคำถามนี้ด้วยstackoverflow.com/q/1178743/168034
phunehehe

2
ดูคำตอบนี้โดยเฉพาะสำหรับคำถาม phunehehe ที่ลิงก์ไป
Tobu

คำตอบ:


69

ฉันคิดว่าการใช้ตัวประมวลผลบริบทในกรณีนี้เป็นการใช้งานมากเกินไป คุณสามารถทำได้อย่างง่ายดาย:

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

แล้ว:

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

และอื่น ๆ ... ดูเหมือนว่าเข้ากันได้กับ DRY


1
ฉันอาจลองทำในวันพรุ่งนี้ - ฉันสงสัยว่าจะบันทึกการทำซ้ำเล็กน้อยในเทมเพลตได้อย่างไรและดูเหมือนว่าจะเป็นแนวทางที่ดี ขอบคุณ
thebiglife

1
แนวทางนี้ดีเยี่ยม ฉันได้แยก base.html ของฉันออกเป็น base.html และ superbase.html ดังนั้นจึงใช้ได้เช่นกันหากคุณต้องการใส่มาร์กอัปหัวเรื่องมาตรฐาน (เช่น h1) ในเทมเพลตที่แชร์ของคุณ เพจยังคงสามารถแทนที่เนื้อหาของบล็อกชื่อและจะอัปเดตในทั้งสองตำแหน่ง
SystemParadox

2
ไม่อนุญาตให้ใช้ข้อความเกินสองครั้งใช่หรือไม่?
Dennis Golomazov

1
Denis Golomazov: ไม่ในกรณีนี้ควรใช้ปลั๊กอินมาโคร (ดูด้านล่าง)
dqd

1
นอกจากนี้ยังสามารถประยุกต์ใช้ในทางอื่น ๆ : การกำหนดh1เนื้อหาภายในบล็อกที่กำหนดไฟล์title. หรือบล็อกที่กำหนดส่วนหนึ่งของไฟล์title.
Mikael Lindlöf

83

ใช้ปลั๊กอินมาโครเทมเพลต Django:

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

หรือ

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>

และ

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

และ

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

2
นี่มันวิเศษมาก! สิ่งนี้สามารถล้างปัญหาที่ฉันได้รับจากการแชร์เทมเพลตกับ django loops และ ajax data loops
กลีเซอรี

1
ทางออกที่ดี. อย่างไรก็ตามมันคือ "use_macro" "usemacro" ผิด
Ramtin

ควรสร้างไว้ใน Django ตามค่าเริ่มต้น
zepp.lee

19

คุณอาจไม่ต้องการใช้บล็อก แต่ต้องการใช้ตัวแปร:

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

จากนั้นคุณตั้งชื่อผ่านบริบท


17
อาจแห้ง แต่คุณไม่ต้องการตั้งชื่อเรื่องในมุมมอง แต่ในเทมเพลต
Lakshman Prasad

6
ควรตั้งชื่อเรื่องจากในเทมเพลตไม่ได้กำหนดโดยบริบทคุณจำเป็นต้องมีวิธีกำหนดตัวแปร "title" นี้มิฉะนั้นจะไม่ใช่วิธีแก้ปัญหาที่ดี
Guillaume Esquevin

นั่นคือสิ่งที่เทมเพลตผู้ดูแลระบบ django ทำ (สำหรับ {{title}}) แต่การกำหนดชื่อตอนนำออกนั้นไม่สะดวก
Tobu

13

นี่คือวิธีที่ฉันค้นพบเมื่อพยายามทำสิ่งเดียวกันด้วยตัวเอง:

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

น่าเสียดายที่ต้องใช้ไฟล์พิเศษ แต่คุณไม่จำเป็นต้องส่งชื่อจากมุมมอง


ในที่สุดฉันก็ตัดสินใจหาโซลูชัน {% macro%} ซึ่งไม่จำเป็นต้องมีไฟล์ใหม่และโดยรวมก็ให้ฉันแสดงสิ่งที่ต้องการแสดงออกได้อย่างชัดเจน
Roman Starkov

เรียบง่ายและมีประสิทธิภาพ ไม่เหมือนคำตอบของ @dqd บล็อกไม่จำเป็นต้องซ้อนกันซึ่งมีประโยชน์มากเช่นสำหรับแท็ก facebook og ซึ่งอาจมีเนื้อหาเหมือนกับแอตทริบิวต์ head อื่น ๆ upvote!
benzkji

2
คำตอบที่ดี สิ่งนี้ดูเหมือนจะแห้งกว่าคำตอบของ @ dqd เนื่องจากคุณไม่จำเป็นต้องทำซ้ำแท็ก <h1> ในแต่ละเทมเพลตที่สืบทอดฐาน นี่อาจเป็นคำตอบที่ได้รับการยอมรับ
Anupam

ยอดเยี่ยม ฉันเคยใช้สิ่งนี้ในสถานการณ์ที่ซับซ้อนมากขึ้นซึ่งฉันต้องการทำซ้ำแถวส่วนท้ายของตารางด้านบน และ<tr>แถวนั้นค่อนข้างซับซ้อน
caram

12

คุณสามารถใช้ได้{% include subtemplate.html %}มากกว่าหนึ่งครั้ง มันไม่เหมือนกับบล็อก แต่เป็นเคล็ดลับ


นี้มีปัญหาเดียวกัน เทมเพลตพื้นฐานจะไม่ทราบว่าจะรวมเทมเพลตย่อยใดไว้ด้วย
Van Gale

โปรดทราบว่าจะช้ากว่าinclude docs.djangoproject.com/th/1.10/topics/performance/…block
Wtower

5

มีการอภิปรายบางส่วนที่นี่: http://code.djangoproject.com/ticket/4529 เห็นได้ชัดว่าทีมหลักของ django ปฏิเสธตั๋วนี้เพราะพวกเขาคิดว่านี่ไม่ใช่สถานการณ์ที่ใช้กันทั่วไป แต่ฉันไม่เห็นด้วย

การทำซ้ำบล็อกนั้นง่ายและสะอาดสำหรับสิ่งนี้: https://github.com/SmileyChris/django-repeatblock

มาโครแม่แบบเป็นอีกหนึ่งมาโครอย่างไรก็ตามผู้เขียนกล่าวว่าไม่ได้ผ่านการทดสอบอย่างรอบคอบ: http://www.djangosnippets.org/snippets/363/

ฉันใช้ repeatblock


4
ดูเหมือนว่าที่เก็บ django-repeatblock ดั้งเดิมจะถูกลบไปแล้ว ส้อมที่ดีที่สุดของที่ดูเหมือนว่าจะgithub.com/phretor/django-repeatblock ; ฉันยังพบgithub.com/ydm/django-sameasซึ่งไม่ต้องการแพทช์ Django 'wontfix'
natevw

4

ขณะที่การปรับปรุงสำหรับทุกคนที่มาในนี้ผมได้นำข้อมูลโค้ดดังกล่าวข้างต้นและกลายเป็นห้องสมุดแท็กแม่แบบ Django-แมโครทำให้แมโครที่มีประสิทธิภาพมากขึ้นและยังใช้รูปแบบบล็อกซ้ำอย่างชัดเจน: Django-แมโคร


4

นี่คือโซลูชันที่มีน้ำหนักเบาคล้ายกับคำตอบด้านบนdo_setและdo_getแท็กเทมเพลต Django ช่วยให้คุณส่งบริบทเทมเพลตทั้งหมดไปยังแท็กซึ่งสามารถให้คุณกำหนดตัวแปรส่วนกลางได้

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

แท็กที่กำหนดเอง (มีแนวคิดที่นี่: https://stackoverflow.com/a/33564990/2747924 ):

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

ยังไม่ลืมที่จะ{% load %}แท็กของคุณเองหรือเพิ่มไปยังตัวเลือกแม่แบบbuiltinsรายการเพื่อให้คุณไม่ต้องโหลดพวกเขาในทุกแม่แบบ ข้อ จำกัด เพียงประการเดียวของแนวทางนี้คือ{% define %}ต้องถูกเรียกจากภายในแท็กบล็อกเนื่องจากเทมเพลตลูกจะแสดงผลแท็กบล็อกที่ตรงกับแท็กหลักเท่านั้น ไม่แน่ใจว่ามีวิธีการประมาณนั้น ตรวจสอบให้แน่ใจว่ามีdefineสายเข้าก่อนที่จะพยายามใช้อย่างชัดเจน


3

จากคำแนะนำของ Van Gale คุณสามารถสร้างรับและตั้งค่าแท็กได้โดยเพิ่มสิ่งต่อไปนี้ในไฟล์ 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)

จากนั้นตั้งค่าในเทมเพลตหนึ่งผ่าน{% set foo %}put data here{% endset %}และรับผ่าน{% get foo %}ในเทมเพลตอื่น


ฉันคิดว่านั่นเป็นทางออกที่ดีที่สุดของทั้งหมด ขอบคุณ Kieran และ Van Gale!
Robert Lacroix

ค่อนข้างเนียน แต่ดูเหมือนว่าการแสดงโหนดทั้งหมดในแท็ก Set อาจจะดีกว่าไม่เช่นนั้นจะแสดงผลซ้ำแล้วซ้ำอีกโดย Get ฉันสามารถนึกถึงเหตุผลที่อาจเป็นความคิดที่ดี (การแสดงผลบล็อกที่เก็บไว้เดียวกันภายในบล็อกที่แตกต่างกันบนหน้าเว็บ) แต่ฉันคิดว่าจะชี้ให้เห็น
acjay

3

ฉันเองก็พบความต้องการ {% block%} ซ้ำ ๆ ในไฟล์เทมเพลตของฉันเช่นกัน ปัญหาคือฉันต้องการให้ใช้ Django {% block%} ในกรณีของ Django แบบมีเงื่อนไขและฉันต้องการให้ {% block%} สามารถเขียนทับได้โดยไฟล์ที่ตามมาซึ่งอาจขยายไฟล์ปัจจุบัน (ดังนั้นในกรณีนี้สิ่งที่ฉันต้องการคือบล็อกมากกว่าตัวแปรเพราะฉันไม่ได้ใช้มันซ้ำในทางเทคนิคมันจะปรากฏที่จุดสิ้นสุดของเงื่อนไข

ปัญหา:

โค้ดเทมเพลต Django ต่อไปนี้จะทำให้เกิดข้อผิดพลาดทางไวยากรณ์ของเทมเพลต แต่ฉันคิดว่า "ต้องการ" ที่ถูกต้องเพื่อให้มีการใช้ {% block%} ที่กำหนดอีกครั้งในเงื่อนไข (IE ทำไมตัวแยกวิเคราะห์ Django จึงตรวจสอบความถูกต้องของไวยากรณ์ทั้งสองอย่าง ของเงื่อนไขไม่ควรตรวจสอบความจริงเท่านั้นหรือไม่)

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

การแก้ไขปัญหา:

คุณสามารถใช้ {% include%} เพื่อแทรก {% block%} ตามเงื่อนไขได้มากกว่าหนึ่งครั้ง สิ่งนี้ใช้ได้ผลสำหรับฉันเพราะตัวตรวจสอบไวยากรณ์ Django มีเฉพาะ TRUTHY {% include%} ดูผลลัพธ์ด้านล่าง:

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


1

มีสองวิธีง่ายๆสำหรับสิ่งนี้

วิธีที่ง่ายที่สุดคือใส่ชื่อของคุณลงในตัวแปรบริบท คุณจะตั้งค่าตัวแปรบริบทในมุมมองของคุณ

หากคุณกำลังใช้สิ่งต่างๆเช่นมุมมองทั่วไปและไม่มี views.py สำหรับรูปภาพแมว ฯลฯ คุณสามารถใช้แท็กเทมเพลตแบบกำหนดเองที่กำหนดตัวแปรในบริบทได้ได้

การไปเส้นทางนี้จะช่วยให้คุณทำสิ่งต่างๆเช่น:

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

จากนั้นใน base.html ของคุณ:

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

อย่างไรก็ตามAny 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.
โจนาธาน

0

คำตอบที่เลือกกล่าวถึงวิธีแก้ปัญหาง่ายๆในการรวมแท็กหนึ่งไว้ในอีกแท็กหนึ่งในเทมเพลตลูกเพื่อให้ทั้งคู่มีค่าเท่ากัน ฉันใช้สิ่งนี้สำหรับรูปภาพทางสังคมเช่นนั้น

แม่แบบเด็ก:

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

จากนั้นในผู้ปกครอง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

ในกิ่งไม้คุณสามารถทำสิ่งนี้:

# 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
นี่คือคำถามเกี่ยวกับ Django
François Constant
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.