web-dev-qa-db-fra.com

Comment implémenter la chapelure dans un template Django?

Certaines solutions fournies lors de la recherche sur Google "Django breadcrumbs" sur Google incluent l’utilisation de templates et block.super, consistant simplement à étendre les blocs de base et à y ajouter la page actuelle. http://www.martin-geber.com/thought/2007/10/25/breadcrumbs-Django-templates/

http://www.djangosnippets.org/snippets/1289/ - fournit une balise de modèle, mais je ne suis pas sûr que cela fonctionnerait si votre urls.py n'était pas correctement déclaré.

Je me demande quel est le meilleur moyen? Et si vous avez déjà mis en place une chapelure, comment l'avez-vous fait?

--- Modifier --

Ma question était censée être la suivante: existe-t-il une méthode généralement acceptée pour faire la chapelure à Django, mais d'après les réponses que je vois, il n'y en a pas, et il existe de nombreuses solutions différentes, je ne sais pas à qui attribuer la bonne réponse, J'ai utilisé une variante de la méthode block.super, alors que toutes les réponses ci-dessous fonctionneraient.

Je suppose qu’il s’agit là d’une question trop subjective.

38
Rasiel

_ {Remarque: je fournis l'extrait complet ci-dessous, car djangosnippets a été capricieux ces derniers temps.} _

Cool, quelqu'un a effectivement trouvé mon extrait :-) L'utilisation de ma balise de template est plutôt simple.

Pour répondre à votre question, il n’existe pas de mécanisme Django "intégré" permettant de gérer le fil d’ariane, mais il nous fournit la meilleure solution: les balises de modèle personnalisées.

Imaginez que vous voulez avoir de la chapelure comme ceci:

Services -> Programming
Services -> Consulting

Ensuite, vous aurez probablement quelques URL nommées: "services", et "programmation", "consulting":

    (r'^services/$',
     'core.views.services',
     {},
     'services'),

    (r'^services/programming$',
     'core.views.programming',
     {},
     'programming'),

    (r'^services/consulting$',
     'core.views.consulting',
     {},
     'consulting'),

Maintenant, dans votre modèle HTML (regardons simplement la page de consultation), tout ce que vous avez à mettre est:

//consulting.html
{% load breadcrumbs %}

{% block breadcrumbs %}
{% breadcrumb_url 'Services' services %}
{% breadcrumb_url 'Consulting' consulting %}

{% endblock %}

Si vous souhaitez utiliser un type de texte personnalisé dans le fil d'Ariane et ne souhaitez pas le lier, vous pouvez utiliser la balise breadcrumb.

//consulting.html
{% load breadcrumbs %}

{% block breadcrumbs %}
  {% breadcrumb_url 'Services' services %}
  {% breadcrumb_url 'Consulting' consulting %}
  {% breadcrumb 'We are great!' %}  
{% endblock %}

Il est plus fréquent que vous souhaitiez inclure un identifiant d'objet particulier, ce qui est également facile à faire. Ceci est un exemple plus réaliste:

{% load breadcrumbs %}

{% block breadcrumbs %}
{% breadcrumb_url 'Employees' employee_list %}
{% if employee.id %}
    {% breadcrumb_url employee.company.name company_detail employee.company.id %}
    {% breadcrumb_url employee.full_name employee_detail employee.id %}
    {% breadcrumb 'Edit Employee ' %}
{% else %}
    {% breadcrumb 'New Employee' %}
{% endif %}

{% endblock %}

Extrait de chapelure DaGood

Fournit deux balises de modèle à utiliser dans vos modèles HTML: breadcrumb et breadcrumb_url. Le premier permet de créer des URL simples, avec la partie texte et la partie url. Ou uniquement du texte non lié (comme le dernier élément du fil d'Ariane, par exemple). Le second peut en fait prendre l'URL nommée avec des arguments! De plus, il faut un titre comme premier argument.

Il s’agit d’un fichier templatetag qui doit aller dans votre répertoire/templatetags.

Il suffit de changer le chemin de l'image dans la méthode create_crumb et vous êtes prêt à partir!

N'oubliez pas de {% charger le fil d'Ariane%} en haut de votre modèle html!

from Django import template
from Django.template import loader, Node, Variable
from Django.utils.encoding import smart_str, smart_unicode
from Django.template.defaulttags import url
from Django.template import VariableDoesNotExist

register = template.Library()

@register.tag
def breadcrumb(parser, token):
    """
    Renders the breadcrumb.
    Examples:
        {% breadcrumb "Title of breadcrumb" url_var %}
        {% breadcrumb context_var  url_var %}
        {% breadcrumb "Just the title" %}
        {% breadcrumb just_context_var %}

    Parameters:
    -First parameter is the title of the crumb,
    -Second (optional) parameter is the url variable to link to, produced by url tag, i.e.:
        {% url person_detail object.id as person_url %}
        then:
        {% breadcrumb person.name person_url %}

    @author Andriy Drozdyuk
    """
    return BreadcrumbNode(token.split_contents()[1:])


@register.tag
def breadcrumb_url(parser, token):
    """
    Same as breadcrumb
    but instead of url context variable takes in all the
    arguments URL tag takes.
        {% breadcrumb "Title of breadcrumb" person_detail person.id %}
        {% breadcrumb person.name person_detail person.id %}
    """

    bits = token.split_contents()
    if len(bits)==2:
        return breadcrumb(parser, token)

    # Extract our extra title parameter
    title = bits.pop(1)
    token.contents = ' '.join(bits)

    url_node = url(parser, token)

    return UrlBreadcrumbNode(title, url_node)


class BreadcrumbNode(Node):
    def __init__(self, vars):
        """
        First var is title, second var is url context variable
        """
        self.vars = map(Variable,vars)

    def render(self, context):
        title = self.vars[0].var

        if title.find("'")==-1 and title.find('"')==-1:
            try:
                val = self.vars[0]
                title = val.resolve(context)
            except:
                title = ''

        else:
            title=title.strip("'").strip('"')
            title=smart_unicode(title)

        url = None

        if len(self.vars)>1:
            val = self.vars[1]
            try:
                url = val.resolve(context)
            except VariableDoesNotExist:
                print 'URL does not exist', val
                url = None

        return create_crumb(title, url)


class UrlBreadcrumbNode(Node):
    def __init__(self, title, url_node):
        self.title = Variable(title)
        self.url_node = url_node

    def render(self, context):
        title = self.title.var

        if title.find("'")==-1 and title.find('"')==-1:
            try:
                val = self.title
                title = val.resolve(context)
            except:
                title = ''
        else:
            title=title.strip("'").strip('"')
            title=smart_unicode(title)

        url = self.url_node.render(context)
        return create_crumb(title, url)


def create_crumb(title, url=None):
    """
    Helper function
    """
    crumb = """<span class="breadcrumbs-arrow">""" \
            """<img src="/media/images/arrow.gif" alt="Arrow">""" \
            """</span>"""
    if url:
        crumb = "%s<a href='%s'>%s</a>" % (crumb, url, title)
    else:
        crumb = "%s&nbsp;&nbsp;%s" % (crumb, title)

    return crumb
43
drozzy

Le module de vue d’administration de Django a des chapelures automatiques, qui sont implémentées comme suit:

{% block breadcrumbs %}
    <div class="breadcrumbs">
        <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
        {% block crumbs %}
            {% if title %} &rsaquo; {{ title }}{% endif %}
        {% endblock %}
    </div>
{% endblock %}

Il existe donc une sorte de support intégré pour cela ..

12
Otto Kekäläinen

Mes fonctions de vue émettent les miettes de pain comme une simple liste.

Certaines informations sont conservées dans la session de l'utilisateur. Indirectement, cependant, il provient de l'URL.

Les miettes de pain ne sont pas une simple liste linéaire de l'endroit où elles se trouvent - c'est à cela que sert l'historique de navigation. Une simple liste des endroits où ils se trouvent ne constitue pas une bonne piste de navigation, car elle ne reflète aucun sens.

Pour la plupart de nos fonctions d'affichage, la navigation est relativement fixe et repose sur la conception de modèle/vue/URL. Dans nos cas, il y a beaucoup de détails, et la chapelure reflète ce rétrécissement - nous avons un «royaume», une «liste», un «parent» et un «enfant». Ils forment une hiérarchie simple allant du général au spécifique.

Dans la plupart des cas, une URL bien définie peut être divisée de manière triviale en une piste de chapelure de Nice. En effet, c’est un test pour une bonne conception d’URL: l’URL peut être interprétée comme un fil d’ariane et affichée de manière significative pour les utilisateurs.

Pour quelques fonctions d'affichage, lorsque nous présentons des informations faisant partie d'une jointure "plusieurs à plusieurs", par exemple, il y a deux parents candidats. L'URL peut dire une chose, mais la pile de contexte de la session en dit une autre. 

Pour cette raison, nos fonctions de vue doivent laisser des indices de contexte dans la session afin que nous puissions émettre des miettes de pain.

6
S.Lott

J'ai eu le même problème et j'ai finalement créé un simple tag Django tempalate: https://github.com/prymitive/bootstrap-breadcrumbs

5
Łukasz Mierzwa

Try Django-breadcrumbs - un middleware enfichable qui ajoute un fil d'Ariane callable/iterable dans votre objet request.

Il prend en charge les vues simples, les vues génériques et l'application Django FlatPages.

5
minder

http://www.djangosnippets.org/snippets/1289/ - fournit une balise de modèle, mais je ne suis pas sûr que cela fonctionnerait si votre urls.py n'était pas correctement déclaré.

Rien ne fonctionnera si votre urls.py n'est pas correctement déclaré. Cela dit, il ne semble pas que ce soit importé de urls.py. En fait, il semble que pour utiliser correctement cette balise, vous devez toujours transmettre au modèle certaines variables. D'accord, ce n'est pas tout à fait vrai: indirectement via la balise url par défaut, que la balise breadcrumb appelle. Mais pour autant que je sache, il n’appelle même pas cette étiquette; toutes les occurrences de url sont des variables créées localement. 

Mais je ne suis pas un expert en analyse de définitions de balises de modèles. Donc, disons quelque part dans le code qu'il reproduit comme par magie la fonctionnalité de la balise url. L'usage semble être que vous passez des arguments à une recherche inversée. Encore une fois, quel que soit votre projet, vous devez urls.py être configuré de sorte que toute vue puisse être atteinte avec une recherche inversée. Cela est particulièrement vrai avec la chapelure. Penses-y:

home > accounts > my account

Les comptes, (jamais} _ doivent-ils contenir une URL arbitraire et codée en dur? Pourrait "mon compte" contient déjà une URL arbitraire, codée en dur? D'une certaine manière, vous allez écrire des chapelures de manière à ce que votre urls.py soit inversé. Cela ne se produira réellement qu'à deux endroits: à votre avis, avec un appel à reverse, ou dans le modèle, avec un appel à une balise de modèle imitant la fonctionnalité de reverse. Il peut y avoir des raisons de préférer le premier au dernier (dans lequel l'extrait lié vous verrouille), mais éviter une configuration logique de votre fichier urls.py n'en fait pas partie.

3
David Berger

Essayez Django-mptt .

Utilitaires pour implémenter MPTT (Modified Preorder Tree Traversal) avec vos classes de modèle Django et utiliser des arbres d'instances de modèle.

3
Tomas Andrle

De toute évidence, pas une seule réponse, mais pour des raisons pratiques, je pense que cela vaut la peine d’envisager la manière naïve. Juste écraser et réécrire tout le fil d'Ariane ... (au moins jusqu'à ce que le Django.contrib.breadcrumb officiel soit publié)

Sans être trop sophistiqué, il vaut mieux garder les choses simples. Cela aide le nouveau venu à comprendre. Il est extrêmement personnalisable (par exemple, vérification de l'autorisation, icône de fil d'ariane, caractères de séparation, fil d'ariane actif, etc.

Modèle de base

<!-- File: base.html -->
<html>
<body>
  {% block breadcrumb %}
  <ul class="breadcrumb">
    <li><a href="{% url 'dashboard:index' %}">Dashboard</a></li>
  </ul>
  {% endblock breadcrumb %}
  {% block content %}{% endblock content %}
</body>
</html>

Modèle d'implémentation

Plus tard, sur chaque page, nous réécrivons et écrasons tout le bloc de fil d'Ariane.

<!-- File: page.html -->
{% extends 'base.html' %}
{% block breadcrumb %}
<ul class="breadcrumb">
  <li><a href="{% url 'dashboard:index' %}">Dashboard</a></li>
  <li><a href="{% url 'dashboard:level-1:index' %}">Level 1</a></li>
  <li class="active">Level 2</li>
</ul>
{% endblock breadcrumb %}

Praticité

Cas d'utilisation de Realworld:

1
Yeo

Vous voudrez peut-être essayer Django-headcrumbs (ne vous inquiétez pas, ils ne vont pas manger votre cerveau).

C’est très léger et très simple à utiliser. Tout ce que vous avez à faire est d’annoter vos vues (car définir la structure des miettes dans les modèles me semble fou) avec un décorateur qui explique comment revenir à la vue donnée.

Voici un exemple tiré de la documentation:

from headcrumbs.decorators import crumb
from headcrumbs.util import name_from_pk

@crumb('Staff')  # This is the root crumb -- it doesn’t have a parent
def index(request):
    # In our example you’ll fetch the list of divisions (from a database)
    # and output it.

@crumb(name_from_pk(Division), parent=index)
def division(request, slug):
    # Here you find all employees from the given division
    # and list them.

Il existe également certaines fonctions utilitaires (par exemple, name_from_pk que vous pouvez voir dans l'exemple) qui génèrent automatiquement des noms de Nice pour vos miettes sans que vous ayez à écrire beaucoup de code.

0
kirelagin

Vous pouvez également réduire le nombre de plaques de chaudière requises pour gérer la chapelure à l'aide de Django-view-breadcrumbs , en ajoutant une propriété de miettes à la vue.

urls.py

urlpatterns = [
    ...
    path('posts/<slug:slug>', views.PostDetail.as_view(), name='post_detail'),
    ...
] 

views.py

from Django.views.generic import DetailView
from view_breadcrumbs import DetailBreadcrumbMixin


class PostDetail(DetailBreadcrumbMixin, DetailView):
    model = Post
    template_name = 'app/post/detail.html'

base.html

{% load Django_bootstrap_breadcrumbs %}

{% block breadcrumbs %}
    {% render_breadcrumbs %}
{% endblock %}
0
jackotonye

J'ai créé un filtre de modèle pour cela.

Appliquez votre filtre personnalisé (je l'ai nommé 'makebreadcrumbs') à request.path comme ceci:

{% with request.resolver_match.namespace as name_space %}
    {{ request.path|makebreadcrumbs:name_space|safe }}
{% endwith %}

Nous devons transmettre l'espace de noms url en tant qu'argument à notre filtre.

Utilisez également le filtre sécurisé, car notre filtre renverra une chaîne qui doit être résolue en tant que contenu HTML.

Le filtre personnalisé devrait ressembler à ceci:

@register.filter
def makebreadcrumbs(value, arg):
    my_crumbs = []
    crumbs = value.split('/')[1:-1]  # slice domain and last empty value
    for index, c in enumerate(crumbs):
        if c == arg and len(crumbs) != 1:  
        # check it is a index of the app. example: /users/user/change_password - /users/ is the index.
            link = '<a href="{}">{}</a>'.format(reverse(c+':index'), c)
        else:
            if index == len(crumbs)-1:
                link = '<span>{}</span>'.format(c)  
                # the current bread crumb should not be a link.
            else:
                link = '<a href="{}">{}</a>'.format(reverse(arg+':' + c), c)
        my_crumbs.append(link)
    return ' &gt; '.join(my_crumbs)  
    # return whole list of crumbs joined by the right arrow special character.

Important:

les parties fractionnées de la 'valeur' ​​dans notre filtre doivent être égales à l'espace de noms dans l'urls.py, afin que la méthode inverse puisse être appelée.

J'espère que ça a aidé.

0
Sevalad

Quelque chose comme cela peut fonctionner dans votre situation:

Capturez l'intégralité de l'URL dans votre vue et créez des liens à partir de celle-ci. Cela nécessitera de modifier votre urls.py, chaque vue nécessitant un fil d'Ariane et vos modèles.

D'abord, vous devez capturer l'URL complète dans votre fichier urls.py

...
(r'^myapp/$', 'myView'),
(r'^myapp/(?P<pk>.+)/$', 'myOtherView'),
...
...
(r'^(?P<whole_url>myapp/)$', 'myView'),
(r'^(?P<whole_url>myapp/(?P<pk>.+)/)$', 'myOtherView'),
...

Ensuite, à votre avis, quelque chose comme:

...
def myView(request, whole_url):
    # dissect the url
    slugs = whole_url.split('/')
    # for each 'directory' in the url create a piece of bread
    breadcrumbs = []
    url = '/'
    for slug in slugs:
        if slug != '':
            url = '%s%s/' % (url, slug)
            breadcrumb = { 'slug':slug, 'url':url }
            breadcrumbs.append(breadcrumb)

    objects = {
        'breadcrumbs': breadcrumbs,
    }
    return render_to_response('myTemplate.html', objects)
...

Ce qui devrait être extrait dans une fonction qui est importée dans les vues qui en ont besoin

Ensuite, dans votre modèle, imprimez la chapelure

...
<div class="breadcrumb-nav">
    <ul>
    {% for breadcrumb in breadcrumbs %}
        <li><a href="{{ breadcrumb.url }}">{{ breadcrumb.slug }}</a></li>
    {% endfor %}
    </ul>
</div>
...

Un inconvénient de le faire de cette façon est que, dans l'état actuel des choses, vous ne pouvez afficher que la partie "répertoire" de l'URL sous forme de texte de lien. Un correctif pour ça (probablement pas un bon choix) serait de garder un dictionnaire dans le fichier qui définit la fonction de fil d'Ariane.

Quoi qu'il en soit, c'est une façon d'accomplir des chapelures, des acclamations :)

0
rennat