web-dev-qa-db-fra.com

Comment répéter un "bloc" dans un modèle Django

Je veux utiliser le même {% block%} deux fois dans le même modèle Django. Je veux que ce bloc apparaître plus d'une fois dans mon modèle de 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>

Et puis étendez-le:

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

J'obtiendrai une exception, car Django veut que le bloc n'apparaisse qu'une seule fois:

TemplateSyntaxError à/

La balise 'block' avec le nom 'title' apparaît plus d'une fois

Une solution rapide et sale serait de dupliquer le bloc title en title1 et title2:

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

Mais c'est une violation du principe SEC . Ce serait très difficile car j'ai beaucoup de modèles hérités, et aussi parce que je ne veux pas aller en enfer ;-)

Y a-t-il une astuce ou une solution à ce problème? Comment puis-je répéter le même bloc dans mon modèle, sans dupliquer tout le code?

121
David Arcos

Je pense que l'utilisation du processeur de contexte est dans ce cas une exagération. Vous pouvez facilement le faire:

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

et alors:

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

et ainsi de suite ... Ressemble à DRY-compatible.

64
dqd

Utilisez le plugin de macros de modèle Django:

http://www.djangosnippets.org/snippets/363/ (Django <1.4)

ou

https://Gist.github.com/1715202 (Django> = 1.4)

Ensuite,

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

et

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

Vous ne voulez probablement pas réellement utiliser un bloc mais plutôt simplement utiliser une variable:

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

Vous définissez ensuite le titre à travers le contexte.

17
Aaron Maenpaa

vous pouvez utiliser {% include subtemplate.html %} plus d'une fois. ce n'est pas la même chose que les blocs, mais fait l'affaire.

12
Javier

Voici un moyen que j'ai découvert en essayant de faire la même chose moi-même:

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

Nécessite un fichier supplémentaire malheureusement, mais ne vous oblige pas à passer le titre de la vue.

11
Roman Starkov

Il y a une discussion ici: http://code.djangoproject.com/ticket/4529 Évidemment Django rejette ce ticket car ils pensent que ce n'est pas un problème courant). scénario utilisé, mais je ne suis pas d'accord.

le bloc de répétition est une implémentation simple et propre pour cela: https://github.com/SmileyChris/Django-repeatblock

les macros de modèle en sont une autre, mais l'auteur a mentionné qu'elle n'a pas été soigneusement testée: http://www.djangosnippets.org/snippets/363/

J'ai utilisé repeatblock.

5
Robert Mao

Comme mise à jour pour tous ceux qui rencontrent cela, j'ai pris l'extrait mentionné ci-dessus et l'ai transformé en une bibliothèque de balises de modèle, Django-macros, rend les macros plus puissantes et implémente également un modèle de bloc répété explicitement: Django- macros .

4
Nick

Voici une solution légère similaire à la précédente do_set et do_get réponse de balise de modèle. Django vous permet de passer tout le contexte du modèle dans une balise qui vous permet de définir une variable globale.

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

balise personnalisée (a eu l'idée ici: https://stackoverflow.com/a/33564990/2747924 ):

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

N'oubliez pas non plus de {% load %} vos balises personnalisées ou ajoutez-les aux options du modèle builtins afin de ne pas avoir à les charger dans chaque modèle. La seule limite à cette approche est le {% define %} doit être appelé à partir d'une balise de bloc car les modèles enfants ne rendent que les balises de bloc qui correspondent aux balises parent. Je ne sais pas s'il y a un moyen de contourner cela. Assurez-vous également que l'appel define vient avant d'essayer de l'utiliser évidemment.

4
manncito

J'ai moi aussi rencontré le même besoin d'un {% block%} répété dans mes fichiers de modèle. Le problème est que je veux qu'un Django {% block%} soit utilisé dans les deux cas d'un Django conditionnel, et je veux le {% block% } pour être écrasable par les fichiers suivants qui peuvent étendre le fichier actuel. (Donc dans ce cas, ce que je veux, c'est définitivement plus un bloc qu'une variable parce que je ne le réutilise pas techniquement, il apparaît juste sur fin d'un conditionnel.

Le problème:

Le code de modèle suivant Django entraînera une erreur de syntaxe de modèle, mais je pense que c'est un "désir" valide d'avoir un {% block%} défini réutilisé dans un conditionnel (IE, pourquoi est le Django valide la syntaxe aux deux extrémités d'un conditionnel, ne devrait-il pas seulement valider la condition VÉRITABLE?)

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

Vous pouvez utiliser un {% include%} pour insérer conditionnellement un {% block%} plus d'une fois. Cela a fonctionné pour moi car le vérificateur de syntaxe Django inclut uniquement le TRUTHY {% include%}. Voir le résultat ci-dessous:

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

En vous basant sur la suggestion de Van Gale, vous pouvez créer des balises get et set en ajoutant ce qui suit à votre fichier 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)

Définissez ensuite les valeurs dans un modèle via {% set foo %}put data here{% endset %} et obtenez-les via {% get foo %} en autre.

3
kieran hervold

Il existe deux solutions simples pour cela.

Le plus simple est de mettre votre titre dans une variable de contexte. Vous définiriez la variable de contexte dans votre vue.

Si vous utilisez quelque chose comme des vues génériques et que vous n'avez pas de vues.py pour les images, les chats, etc., vous pouvez suivre la voie d'un balise de modèle personnalisée qui définit une variable dans le contexte .

Suivre cette route vous permettrait de faire quelque chose comme:

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

Puis dans votre base.html:

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

J'utilise cette réponse pour garder les choses au sec.

{% extends "base.html" %}

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