languages - Cómo repetir un "bloque" en una plantilla django
templates dirs django (13)
Quiero usar el mismo {% block%} dos veces en la misma plantilla django. Quiero que este bloque aparezca más de una vez en mi plantilla base:
# base.html
<html>
<head>
<title>{% block title %}My Cool Website{% endblock %}</title>
</head>
<body>
<h1>{% block title %}My Cool Website{% endblock %}</h1>
</body>
</html>
Y luego extenderlo:
# 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 %}
Obtendré una excepción, ya que Django quiere que el bloque aparezca solo una vez:
TemplateSyntaxError at /
La etiqueta ''bloque'' con el nombre ''título'' aparece más de una vez
Una solución rápida y sucia sería duplicar el título del bloque en title1 y title2 :
# blog.html
{% extends ''base.html'' %}
{% block title1 %}My Blog{% endblock %}
{% block title2 %}My Blog{% endblock %}
Pero esto es una violación del principio DRY . Sería muy difícil ya que tengo muchas plantillas heredadas, y también porque no quiero ir al infierno ;-)
¿Hay algún truco o solución para este problema? ¿Cómo puedo repetir el mismo bloque en mi plantilla, sin duplicar todo el código?
Aquí hay una solución liviana similar a la respuesta de la etiqueta de plantilla do_set
y do_get
anterior. Django le permite pasar todo el contexto de la plantilla a una etiqueta que puede permitirle definir una variable 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 %}
etiqueta personalizada (tengo la idea aquí: https://.com/a/33564990/2747924 ):
@register.simple_tag(takes_context=True)
def define(context, key, value):
context.dicts[0][key] = value
return ''''
Además, no te olvides de {% load %}
tus etiquetas personalizadas o builtins lista de opciones builtins opciones de la plantilla para que no tengas que cargarlas en cada plantilla. La única limitación de este enfoque es que se debe llamar a {% define %}
desde una etiqueta de bloque porque las plantillas secundarias solo muestran etiquetas de bloques que coinciden con las etiquetas principales. No estoy seguro de si hay una forma de evitar eso. También asegúrese de que la llamada a define
aparezca antes de intentar usarla obviamente.
Como una actualización para cualquiera que se encuentre con esto, tomé el fragmento mencionado anteriormente y lo convertí en una biblioteca de etiquetas de plantilla, django-macros, hace que las macros sean más potentes y también implementa explícitamente un patrón de bloque repetido: github.com/nalourie/django-macros .
Creo que el uso del procesador de contexto es en este caso una exageración. Puedes hacerlo fácilmente:
#base.html
<html>
<head>
<title>{% block title %}My Cool Website{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
y entonces:
# blog.html
{% extends ''base.html'' %}
{% block content %}
<h1>{% block title %}My Blog{% endblock %}</h1>
Lorem ipsum here...
{% endblock %}
y así sucesivamente ... Parece compatible con DRY.
En twig puedes hacer esto como:
# 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 %}
Esta es una forma que descubrí cuando intentaba hacer lo mismo:
# 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 %}
Requiere un archivo adicional, lamentablemente, pero no requiere que pase el título de la vista.
Hay algunas discusiones aquí: http://code.djangoproject.com/ticket/4529 Obviamente, el equipo central de django rechaza este ticket porque piensan que este no es un escenario común utilizado, sin embargo no estoy de acuerdo.
repetir bloque es una implementación simple y limpia para esto: https://github.com/SmileyChris/django-repeatblock
la plantilla de macros es otra, sin embargo, el autor mencionó que no se probó cuidadosamente: http://www.djangosnippets.org/snippets/363/
Utilicé repeatblock.
Hay dos soluciones fáciles para esto.
Lo más fácil es poner su título en una variable de contexto. Usted establecería la variable de contexto en su vista.
Si está utilizando algo así como vistas genéricas y no tiene un views.py para imágenes, gatos, etc., puede ir por el camino de una etiqueta de plantilla personalizada que establece una variable en el contexto .
Seguir esta ruta te permitiría hacer algo como:
{% extends "base.html" %}
{% load set_page_title %}
{% page_title "My Pictures" %}
...
Luego en tu base.html:
...
{% block title %}{{ page_title }}{% endblock %}
...
<h1>{{ page_title }}</h1>
Probablemente no quiera usar un bloque sino simplemente usar una variable:
# base.html
<html>
<head>
<title>{{ title|default:"My Cool Website" }}</title>
</head>
<body>
<h1>{{ title|default:"My Cool Website" }}</h1>
</body>
</html>
Luego configura el título a través del contexto.
Sobre la base de la sugerencia de Van Gale, puede crear obtener y establecer etiquetas agregando lo siguiente a su archivo 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)
Luego, establezca los valores en una plantilla a través de {% set foo %}put data here{% endset %}
y {% set foo %}put data here{% endset %}
a través de {% get foo %}
en otra.
Usa el plugin macros de la plantilla Django:
http://www.djangosnippets.org/snippets/363/ (django <1.4)
o
https://gist.github.com/1715202 (django> = 1.4)
Entonces,
# 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>
y
# blog.html
{% extends ''base.html'' %}
{% block title %}My Blog{% endblock %}
Utilizo esta respuesta para mantener las cosas secas.
{% extends "base.html" %}
{% with "Entry Title" as title %}
{% block title %}{{ title }}{% endblock %}
{% block h1 %}{{ title }}{% endblock %}
{% endwith %}
Yo también he encontrado la misma necesidad de repetir {% block%} en mis archivos de plantilla. El problema es que quiero que se use un Django {% block%} en cualquier caso de un condicional de Django, y quiero que el {% block%} sea sobrescribible por archivos posteriores que pueden extender el archivo actual. (Entonces, en este caso, lo que quiero es definitivamente más un bloque que una variable porque técnicamente no lo estoy reutilizando, simplemente aparece en cualquier extremo de un condicional.
El problema:
El siguiente código de plantilla de Django dará como resultado un Error de sintaxis de plantilla, pero creo que es un "querer" válido tener un {% block%} reutilizado en un condicional (IE, ¿por qué la sintaxis de validación del analizador de Django en AMBOS extremos de un condicional, ¿no debería solo validar la condición TRUTHY?)
# 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 %}
La solución:
Puede usar un {% include%} para insertar condicionalmente un {% block%} más de una vez. Esto funcionó para mí porque el verificador de sintaxis de Django incluye solo la VERDAD {% include%}. Vea el resultado a continuación:
# 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 %}
puede usar {% include subtemplate.html %}
más de una vez. no es lo mismo que bloques, pero funciona.