Cách lặp lại một khối khối trong một mẫu django


126

Tôi muốn sử dụng cùng {% block%} hai lần trong cùng một mẫu django. Tôi muốn khối này xuất hiện nhiều lần trong mẫu cơ sở của mình:

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

Và sau đó mở rộng nó:

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

Tôi sẽ có một ngoại lệ, vì Django muốn khối chỉ xuất hiện một lần:

TemplateSyntaxError tại /

thẻ 'chặn' với tên 'title' xuất hiện nhiều lần

Một giải pháp nhanh chóng và bẩn thỉu sẽ sao chép tiêu đề khối thành title1title2 :

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

Nhưng đây là một sự vi phạm nguyên tắc DRY . Sẽ rất khó vì tôi có rất nhiều mẫu kế thừa, và cũng vì tôi không muốn xuống địa ngục ;-)

Có bất kỳ mẹo hoặc giải quyết vấn đề này? Làm cách nào tôi có thể lặp lại cùng một khối trong mẫu của mình mà không cần sao chép tất cả mã?


1
cũng xem giải pháp cho câu hỏi này stackoverflow.com/q/1178743/168034
phunehehe

2
Xem câu trả lời này đặc biệt cho các câu hỏi liên kết phunehehe đến.
Tobu

Câu trả lời:


69

Tôi nghĩ rằng việc sử dụng bộ xử lý bối cảnh trong trường hợp này là quá mức cần thiết. Bạn có thể dễ dàng làm điều này:

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

và sau đó:

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

và v.v ... Có vẻ như tương thích với DRY.


1
Tôi có thể thử điều này vào ngày mai - Tôi đã tự hỏi làm thế nào để tiết kiệm một chút sự lặp lại trong các mẫu và đây có vẻ là một cách tiếp cận tốt. cảm ơn.
thebiglife

1
Cách tiếp cận này là tuyệt vời. Tôi đã tách cơ sở.html của tôi thành base.html và awesomease.html, do đó, điều này cũng hoạt động nếu bạn muốn đặt một đánh dấu tiêu đề tiêu chuẩn (như h1) trong các mẫu được chia sẻ của bạn. Các trang vẫn có thể ghi đè nội dung của khối tiêu đề và nó sẽ cập nhật ở cả hai vị trí.
SystemParadox

2
Điều này không cho phép sử dụng văn bản nhiều hơn hai lần, phải không?
Dennis Golomazov

1
Denis Golomazov: Không. Trong trường hợp đó, tốt hơn là sử dụng plugin macro (xem bên dưới).
dqd

1
Cũng có thể được áp dụng theo cách khác: xác định h1nội dung bên trong khối xác định title. Hoặc một khối xác định một phần của title.
Mikael Lindlöf

83

Sử dụng plugin macro mẫu Django:

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

hoặc là

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
Cái này thật tuyệt! Điều này thực sự có thể làm sạch các vấn đề tôi gặp phải khi chia sẻ các mẫu với các vòng lặp django và các vòng lặp dữ liệu ajax.
Glycerine

1
Giải pháp tốt. Tuy nhiên, đó là "use_macro". "Usemacro" là sai.
Ramtin

Chắc chắn nên được xây dựng vào Django theo mặc định.
zepp.lee

19

Bạn có thể không thực sự muốn sử dụng một khối mà chỉ sử dụng một biến:

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

Sau đó, bạn đặt tiêu đề thông qua bối cảnh.


17
Có lẽ là khô. Nhưng bạn sẽ không muốn đặt tiêu đề trong chế độ xem; nhưng trong các mẫu.
Lakshman Prasad

6
Các tiêu đề nên được đặt từ bên trong các mẫu, không được cung cấp theo ngữ cảnh, bạn cần có cách xác định biến "tiêu đề" này, nếu không đây không phải là một giải pháp tốt.
Guillaume Esquevin

Đó là những gì các mẫu quản trị django làm (cho {{title}}), nhưng việc xác định tiêu đề khi xóa là bất tiện.
Tobu

13

Đây là một cách tôi tự khám phá khi cố gắng làm điều tương tự:

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

Không may yêu cầu một tệp bổ sung, nhưng không yêu cầu bạn chuyển tiêu đề từ chế độ xem.


Cuối cùng, tôi đã giải quyết cho giải pháp {% macro%}, không yêu cầu tệp mới và nói chung cho phép tôi thể hiện chính xác những gì tôi muốn thể hiện.
Roman Starkov

simpel và hiệu quả. Không giống như câu trả lời của @dqd, các khối không cần lồng nhau, ví dụ rất hữu ích cho các thẻ og của facebook, có thể có cùng nội dung với các thuộc tính đầu khác. nâng cao!
benzkji

2
Câu trả lời chính xác. Điều này dường như thậm chí DRYer hơn câu trả lời của @ dqd vì bạn không phải lặp lại thẻ <h1> trong mỗi mẫu kế thừa cơ sở. Đây có thể là câu trả lời được chấp nhận.
Anupam

Thông minh! Tôi đã sử dụng điều này trong các tình huống phức tạp hơn khi tôi muốn lặp lại hàng chân trang của một bảng trên cùng. Và <tr>hàng khá phức tạp.
caram


5

Có một số cuộc thảo luận ở đây: http://code.djangoproject.com/ticket/4529 Rõ ràng đội ngũ cốt lõi django từ chối vé này vì họ nghĩ rằng đây không phải là một kịch bản được sử dụng phổ biến, tuy nhiên tôi không đồng ý.

khối lặp lại là cách thực hiện đơn giản và rõ ràng cho việc này: https://github.com/SmileyChris/django-repeatblock

macro mẫu là một cái khác, tuy nhiên tác giả đã đề cập rằng nó không được kiểm tra cẩn thận: http://www.djangosnippets.org/snippets/363/

Tôi đã sử dụng lặp lại.


4
Kho lưu trữ django-repeatblock ban đầu dường như đã bị xóa. Cái ngã ba tốt nhất trong số đó dường như là github.com/phretor/django-repeatblock ; Tôi cũng tìm thấy github.com/ydm/django-sameas , không yêu cầu bản vá Django 'wontfix'.
natevw

4

Là một bản cập nhật cho bất kỳ ai gặp phải điều này, tôi đã lấy đoạn trích được đề cập ở trên và biến nó thành một thư viện thẻ mẫu, django-macros, làm cho các macro mạnh hơn và cũng thực hiện một mô hình khối lặp đi lặp lại rõ ràng: django-macro .


4

Đây là một giải pháp nhẹ tương tự như câu trả lời ở trên do_setdo_getthẻ mẫu. Django cho phép bạn chuyển toàn bộ bối cảnh mẫu vào một thẻ có thể cho phép bạn xác định một biến toàn cục.

cơ sở.html:

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

trang.html:

{% extends "base.html" %}

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

thẻ tùy chỉnh (có ý tưởng ở đây: https://stackoverflow.com/a/33564990/2747924 ):

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

Cũng đừng quên {% load %}thẻ tùy chỉnh của bạn hoặc thêm chúng vào các tùy chọn mẫu dựng sẵn danh sách, do đó bạn không cần phải tải chúng trong mỗi mẫu. Hạn chế duy nhất của phương pháp này là {% define %}phải được gọi từ trong thẻ khối vì các mẫu con chỉ hiển thị các thẻ khối khớp với thẻ cha. Không chắc chắn nếu có một cách xung quanh đó. Cũng đảm bảo rằng definecuộc gọi đến trước khi bạn cố gắng sử dụng nó rõ ràng.


3

Dựa trên đề xuất của Van Gale, bạn có thể tạo các thẻ get và set bằng cách thêm các mục sau vào tệp 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)

Sau đó, đặt các giá trị trong một mẫu thông qua {% set foo %}put data here{% endset %}và đưa chúng qua {% get foo %}một mẫu khác.


Tôi nghĩ đó là giải pháp tao nhã nhất trong tất cả. Cảm ơn Kieran và Van Gale!
Robert Lacroix

Điều đó khá thú vị, nhưng có vẻ như còn tốt hơn khi kết xuất tất cả các nút trong thẻ Set, nếu không, chúng sẽ được kết xuất nhiều lần bằng Get. Tôi có thể nghĩ ra những lý do có thể là một ý tưởng hay (hiển thị cùng một khối được lưu trữ bên trong các khối khác nhau trên một trang), nhưng tôi chỉ nghĩ rằng tôi đã chỉ ra nó.
acjay

3

Tôi cũng đã gặp phải cùng một nhu cầu đối với {% block%} được lặp lại trong các tệp mẫu của mình. Vấn đề là tôi muốn Django {% block%} được sử dụng trong cả hai trường hợp có điều kiện Django và tôi muốn {% block%} có thể ghi đè lên bởi các tệp tiếp theo có thể mở rộng tệp hiện tại. (Vì vậy, trong trường hợp này, thứ tôi muốn chắc chắn là nhiều khối hơn là một biến vì tôi không sử dụng lại về mặt kỹ thuật, nó chỉ xuất hiện ở hai đầu của một điều kiện.

Vấn đề:

Mã mẫu Django sau đây sẽ dẫn đến Lỗi Cú pháp Mẫu, nhưng tôi nghĩ rằng đó là "muốn" hợp lệ để sử dụng lại {% block%} được xác định trong một điều kiện (IE, tại sao cú pháp xác thực trình phân tích cú pháp Django trên BOTH kết thúc trong một điều kiện, không nên chỉ xác nhận điều kiện SỰ THẬT?)

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

Giải pháp:

Bạn có thể sử dụng {% bao gồm%} để chèn một cách có điều kiện {% block%} nhiều lần. Điều này làm việc cho tôi vì trình kiểm tra cú pháp Django chỉ bao gồm TRUTHY {% bao gồm%}. Xem kết quả dưới đây:

# 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

Tôi sử dụng câu trả lời này để giữ cho mọi thứ khô ráo.

{% extends "base.html" %}

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

1

Có hai giải pháp dễ dàng cho việc này.

Đơn giản nhất là đặt tiêu đề của bạn vào một biến bối cảnh. Bạn sẽ đặt biến bối cảnh trong chế độ xem của bạn.

Nếu bạn đang sử dụng một cái gì đó như chế độ xem chung và không có lượt xem cho hình ảnh, mèo, v.v. thì bạn có thể đi theo cách của thẻ mẫu tùy chỉnh đặt một biến trong ngữ cảnh .

Đi theo tuyến đường này sẽ cho phép bạn làm một cái gì đó như:

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

Sau đó, trong cơ sở của bạn.html:

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

Tuy nhiênAny 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

Câu trả lời được chọn ám chỉ một cách giải quyết dễ dàng để bọc một thẻ bên trong một thẻ khác trong mẫu con để cung cấp cho cả hai cùng một giá trị. Tôi sử dụng điều này cho hình ảnh xã hội như vậy.

Mẫu con:

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

Sau đó, trong cha mẹ 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

Trong twig bạn có thể làm điều này như sau:

# 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
Đây là một câu hỏi về Django.
François Constant
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.