Navegação em Django


104

Acabei de fazer meu primeiro pequeno webapp em Django e adorei. Estou prestes a começar a converter um antigo site PHP de produção em django e, como parte de seu modelo, há uma barra de navegação.

Em PHP, eu verifico o URL de cada opção de navegação em relação ao URL atual, no código do modelo e aplico uma classe CSS se eles estiverem alinhados. É terrivelmente confuso.

Existe algo melhor para django ou uma boa maneira de lidar com o código no modelo?

Para começar, como faria para obter o URL atual?


Eu criei github.com/orokusaki/django-active-menu para isso - ele oferece suporte a estruturas de URL aninhadas e depende de configuração em vez de convenção (por pior que pareça), para que você possa definir a hierarquia de seu site como quiser. Você acabou de usar <a href="{% url "view:name" %}" {% active_class "view:name" %}>. Você pode opcionalmente usá-lo para gerar apenas o " active"valor (passando Falsecomo um segundo argumento para a tag) para anexar a um atributo de classe existente, mas para a maioria dos links de navegação, esse exemplo é o que eu uso.
orokusaki

Esta pergunta parece estar relacionada a esta stackoverflow.com/a/9801473/5739875
Evgeny Bobkin

Respostas:


74

Eu uso a herança do modelo para personalizar a navegação. Por exemplo:

base.html

<html>
    <head>...</head>
    <body>
        ...
        {% block nav %}
        <ul id="nav">
            <li>{% block nav-home %}<a href="{% url home %}">Home</a>{% endblock %}</li>
            <li>{% block nav-about %}<a href="{% url about %}">About</a>{% endblock %}</li>
            <li>{% block nav-contact %}<a href="{% url contact %}">Contact</a>{% endblock %}</li>
        </ul>
        {% endblock %}
        ...
    </body>
</html>

about.html

{% extends "base.html" %}

{% block nav-about %}<strong class="nav-active">About</strong>{% endblock %}

Gosto muito dessa ideia, especialmente pela flexibilidade, mas ela vem com a compensação menos SECA. Eu comecei a usar isso em um site embora.
covarde anônimo,

23
Não estou entusiasmado com essa abordagem porque não é incomum ter várias seções do site manipuladas pelo mesmo submodelo. Então você acaba colocando vars personalizados em visualizações e condicionais em modelos, ou reorganizando submodelos para que sejam todos únicos ... tudo apenas para detectar a seção do site atual. A abordagem da tag de modelo acaba sendo mais limpa no final.
shacker de

Eu olhei para algumas outras soluções e parece que todas elas são um pouco hackeadas. Este, pelo menos, é bastante direto e simples de implementar / descartar.
mlissner

Refatorei o <ul id="nav">....</ul>para um arquivo diferente, digamos tabs.html. Portanto, agora base.html contido {%block nav%}{%include "tabs.html"%}{%endblock%}e, em seguida, o destaque da guia ativa parou de funcionar (em about.html acima). Estou perdendo alguma coisa?
Nenhum-da

@Maddy, você tem vias indiretas o suficiente para que eu não tenha certeza absoluta de que estou mantendo isso direto na minha cabeça, mas acho que a resposta tem a ver com como a includetag funciona. Verifique a nota incluída nos documentos: docs.djangoproject.com/en/dev/ref/templates/builtins/#include No seu caso, no momento em que você está tentando substituir o modelo base em about.html, acho que você já tem um bloco HTML renderizado, ao invés de um bloco de template Django esperando para ser processado.
jpwatts

117

Você não precisa de um if para fazer isso, dê uma olhada no seguinte código:

tags.py

@register.simple_tag
def active(request, pattern):
    import re
    if re.search(pattern, request.path):
        return 'active'
    return ''

urls.py

urlpatterns += patterns('',
    (r'/$', view_home_method, 'home_url_name'),
    (r'/services/$', view_services_method, 'services_url_name'),
    (r'/contact/$', view_contact_method, 'contact_url_name'),
)

base.html

{% load tags %}

{% url 'home_url_name' as home %}
{% url 'services_url_name' as services %}
{% url 'contact_url_name' as contact %}

<div id="navigation">
    <a class="{% active request home %}" href="{{ home }}">Home</a>
    <a class="{% active request services %}" href="{{ services }}">Services</a>
    <a class="{% active request contact %}" href="{{ contact }}">Contact</a>
</div>

é isso aí. para detalhes de implementação, dê uma olhada em:
gnuvince.wordpress.com
110j.wordpress.com


2
As propriedades do href estão sem os colchetes do modelo django {{,}}. Por exemplo, <a class="{% active request home %}" href="home"> Home </a> deve ser, <a class = "{% active request home%}" href = "{{home} } "> Início </a>, o arquivo tags.py também precisará de algumas inclusões. Caso contrário, ótima solução!
bsk

2
+1 Isso é mais fracamente acoplado de aplicativos. Como um iniciante, descobri que as tags precisam de seu próprio aplicativo, você não pode simplesmente despejar isso em um arquivo tags.py global. Criei um novo aplicativo chamado tags e tudo correu bem. docs.djangoproject.com/en/dev/howto/custom-template-tags
Keyo

3
@Keyo, crie um diretório templatetags em seu projeto e adicione seu projeto aos aplicativos instalados. Isso também resolverá o problema. Como alternativa, como você disse, crie seu site principal como um aplicativo dentro do seu projeto.
Josh Smeaton,

5
Não se esqueça de adicionar django.core.context_processors.requestao seu TEMPLATE_CONTEXT_PROCESSORSinsettings.py
amigcamel

1
Isso é inválido para estados que podem ser aninhados, por exemplo, mysite.com(como home) e mysite.com/blog, pois o caminho será mostrado como /e /blog/(respectivamente), resultando em uma correspondência para o anterior a cada vez. Se você não usar /como um patamar, pode estar tudo bem, caso contrário, eu apenas uso return 'active' if pattern == request.path else ''(ainda não vi problemas com isso, mas acabei de configurar usando isso).
nerdwaller de

33

Gostei da limpeza do 110j acima, então peguei a maior parte dele e refatorei para resolver os 3 problemas que tive com ele:

  1. a expressão regular correspondia ao url 'inicial' a todos os outros
  2. Eu precisava de vários URLs mapeados para uma guia de navegação , então precisava de uma tag mais complexa que recebesse uma quantidade variável de parâmetros
  3. consertou alguns problemas de url

Aqui está:

tags.py:

from django import template

register = template.Library()

@register.tag
def active(parser, token):
    args = token.split_contents()
    template_tag = args[0]
    if len(args) < 2:
        raise template.TemplateSyntaxError, "%r tag requires at least one argument" % template_tag
    return NavSelectedNode(args[1:])

class NavSelectedNode(template.Node):
    def __init__(self, patterns):
        self.patterns = patterns
    def render(self, context):
        path = context['request'].path
        for p in self.patterns:
            pValue = template.Variable(p).resolve(context)
            if path == pValue:
                return "active" # change this if needed for other bootstrap version (compatible with 3.2)
        return ""

urls.py:

urlpatterns += patterns('',
    url(r'/$', view_home_method, {}, name='home_url_name'),
    url(r'/services/$', view_services_method, {}, name='services_url_name'),
    url(r'/contact/$', view_contact_method, {}, name='contact_url_name'),
    url(r'/contact/$', view_contact2_method, {}, name='contact2_url_name'),
)

base.html:

{% load tags %}

{% url home_url_name as home %}
{% url services_url_name as services %}
{% url contact_url_name as contact %}
{% url contact2_url_name as contact2 %}

<div id="navigation">
    <a class="{% active request home %}" href="home">Home</a>
    <a class="{% active request services %}" href="services">Services</a>
    <a class="{% active request contact contact2 %}" href="contact">Contact</a>
</div>

Talvez seja melhor respondermos com Marcus um, mas como funciona com a "casa"? está sempre ativo? Como torná-lo ativo apenas na chamada do URL raiz (www.toto.com/ e www.toto.com/index)? Ambas as respostas não resultam neste problema ...
DestyNova

20

Eu sou o autor de django-lineage que escrevi especificamente para resolver esta questão: D

Fiquei aborrecido ao usar o método jpwatts (perfeitamente aceitável) em meus próprios projetos e me inspirei na resposta do 110j. A linhagem se parece com isto:

{% load lineage %}
<div id="navigation">
    <a class="{% ancestor '/home/' %}" href="/home/">Home</a>
    <a class="{% ancestor '/services/' %}" href="/services/">Services</a>
    <a class="{% ancestor '/contact/' %}" href="/contact/">Contact</a>
</div>

ancestor é simplesmente substituído por "ativo" se o argumento corresponder ao início do URL da página atual.

Argumentos variáveis ​​e {% url %}resolução reversa de tipo completo também são suportados. Eu polvilhei algumas opções de configuração, desenvolvi um pouco e empacotei para que todos possam usar.

Se alguém estiver interessado, leia um pouco mais sobre isso em:

>> github.com/marcuswhybrow/django-lineage


1
caminhos de codificação rígidos para o modelo :(
CpILL

10

Desde Django 1.5 :

Em todas as visualizações genéricas baseadas em classe (ou em qualquer visualização baseada em classe herdada de ContextMixin), o dicionário de contexto contém uma variável de visualização que aponta para a instância de Visualização.

Portanto, se você estiver usando essas visualizações, poderá adicionar algo breadcrumbscomo um campo de nível de classe e usá-lo em seus modelos.

Exemplo de código de visualização:

class YourDetailView(DetailView):
     breadcrumbs = ['detail']
     (...)

Em seu modelo, você pode usá-lo desta forma:

<a href="/detail/" {% if 'detail' in view.breadcrumbs %}class="active"{% endif %}>Detail</a>

Se você deseja "destacar" itens de navegação pai adicionalmente, é necessário estender a breadcrumbslista:

class YourDetailView(DetailView):
     breadcrumbs = ['dashboard', 'list', 'detail']
     (...)

... e em seu modelo:

<a href="/dashboard/" {% if 'dashboard' in view.breadcrumbs %}class="active"{% endif %}>Dashboard</a>
<a href="/list/" {% if 'list' in view.breadcrumbs %}class="active"{% endif %}>List</a>
<a href="/detail/" {% if 'detail' in view.breadcrumbs %}class="active"{% endif %}>Detail</a>

Esta é uma solução fácil e limpa e funciona muito bem com navegação aninhada.


Nesse exemplo, não seriam todos os três itens de navegação .active?
Oli

Sim, mas normalmente é isso que você deseja alcançar com a navegação de vários níveis. Você pode, é claro, colocar um item breadcrumbsse quiser. Mas você tem razão - meu exemplo não é o melhor.
Konrad Hałas

Exemplo melhorado da @Oli.
Konrad Hałas

9

Você pode aplicar uma classe ou id ao elemento do corpo da página, em vez de a um item de navegação específico.

HTML:

<body class="{{ nav_class }}">

CSS:

body.home #nav_home,
body.about #nav_about { */ Current nav styles */ }

8

Eu faço assim:

<a class="tab {% ifequal active_tab "statistics" %}active{% endifequal %}" href="{% url Member.Statistics %}">Statistics</a>

e então tudo que tenho a fazer é, na minha opinião, adicionar {'active_tab': 'statistics'}ao meu dicionário de contexto.

Se estiver usando, RequestContextvocê pode obter o caminho atual em seu modelo como:

{{ request.path }}

E na sua opinião:

from django.template import RequestContext

def my_view(request):
    # do something awesome here
    return template.render(RequestContext(request, context_dict))

Obrigado por compartilhar esta informação. Usei este método, mas também tinha uma página plana na minha barra de navegação, então para detectar isso e realçá-la corretamente, usei {% ifequal flatpage.url '/ about /'%}. Não gosto da detecção codificada da URL, mas funciona para um hack único.
Matt Garrison

O problema com essa solução é que você tem "estatísticas" embutidas no código. Isso anula o propósito de usar a tag url para obter o url da página.
justin

7

Peguei o código do nivhab acima e removi algumas estranhezas e o transformei em um templatetag limpo, modifiquei para que / account / edit / ainda torne / account / tab ativo.

#current_nav.py
from django import template

register = template.Library()

@register.tag
def current_nav(parser, token):
    import re
    args = token.split_contents()
    template_tag = args[0]
    if len(args) < 2:
        raise template.TemplateSyntaxError, "%r tag requires at least one argument" % template_tag
    return NavSelectedNode(args[1])

class NavSelectedNode(template.Node):
    def __init__(self, url):
        self.url = url

    def render(self, context):
        path = context['request'].path
        pValue = template.Variable(self.url).resolve(context)
        if (pValue == '/' or pValue == '') and not (path  == '/' or path == ''):
            return ""
        if path.startswith(pValue):
            return ' class="current"'
        return ""



#template.html
{% block nav %}
{% load current_nav %}
{% url home as home_url %}
{% url signup as signup_url %}
{% url auth_login as auth_login_url %}
<ul class="container">
    <li><a href="{{ home_url }}"{% current_nav home_url %} title="Home">Home</a></li>
    <li><a href="{{ auth_login_url }}"{% current_nav auth_login_url %} title="Login">Login</a></li>
    <li><a href="{{ signup_url }}"{% current_nav signup_url %} title="Signup">Signup</a></li>
</ul>
{% endblock %}

6

Esta é apenas uma variante da solução css proposta por Toba acima:

Inclua o seguinte em seu modelo básico:

<body id="section-{% block section %}home{% endblock %}">

Então, em seus modelos que estendem o uso básico:

{% block section %}show{% endblock %}

Você pode então usar css para destacar a área atual com base na tag do corpo (por exemplo, se tivermos um link com um id de nav-home):

#section-home a#nav-home{
 font-weight:bold;
}


3

Obrigado por suas respostas até agora, senhores. Eu optei por algo um pouco diferente novamente ..

No meu modelo:

<li{{ link1_active }}>...link...</li>
<li{{ link2_active }}>...link...</li>
<li{{ link3_active }}>...link...</li>
<li{{ link4_active }}>...link...</li>

Depois de descobrir em qual página estou na lógica (geralmente em urls.py), passo class="selected"como parte do contexto sob o nome correto para o modelo.

Por exemplo, se eu estiver na página link1, vou anexar {'link1_active':' class="selected"'} ao contexto do modelo para coletar e injetar.

Parece funcionar e está bastante limpo.

Editar: para manter o HTML fora do meu controlador / visualização, modifiquei um pouco:

<li{% if link1_active %} class="selected"{% endif %}>...link...</li>
<li{% if link2_active %} class="selected"{% endif %}>...link...</li>
...

Isso torna o modelo um pouco menos legível, mas concordo, é melhor não empurrar HTML bruto do arquivo de urls.


2
Você realmente deve evitar manipular HTML bruto em sua visualização, que é o que essa técnica requer. Você já pensou em escrever uma tag de modelo personalizado?
Justin Voss

Você está certo. Eu editei para parar de passar pelo HTML. Acabei de passar por True agora. Ainda não escrevi nenhuma tag de modelo, mas sim, pode ser um bom lugar para começar.
Oli

2

Tenho vários menus na mesma página que são criados dinamicamente por meio de um loop. As postagens acima relacionadas ao contexto me deram uma solução rápida. Espero que isso ajude alguém. (Eu uso isso além da tag de modelo ativa - minha correção resolve o problema dinâmico). Parece uma comparação boba, mas funciona. Eu escolhi nomear as variáveis ​​active_something-unique e something-unique, dessa forma funciona com menus aninhados.

Aqui está uma parte da visualização (o suficiente para entender o que estou fazendo):

def project_list(request, catslug):
    "render the category detail page"
    category = get_object_or_404(Category, slug=catslug, site__id__exact=settings.SITE_ID)
    context = {
        'active_category': 
            category,
        'category': 
            category,
        'category_list': 
            Category.objects.filter(site__id__exact=settings.SITE_ID),

    }

E isso é do modelo:

<ul>
  {% for category in category_list %}
    <li class="tab{% ifequal active_category category %}-active{% endifequal %}">
      <a href="{{ category.get_absolute_url }}">{{ category.cat }}</a>
    </li>
  {% endfor %}
</ul>

2

Minha solução foi escrever um processador de contexto simples para definir uma variável com base no caminho da solicitação:

def navigation(request):
"""
Custom context processor to set the navigation menu pointer.
"""
nav_pointer = ''
if request.path == '/':
    nav_pointer = 'main'
elif request.path.startswith('/services/'):
    nav_pointer = 'services'
elif request.path.startswith('/other_stuff/'):
    nav_pointer = 'other_stuff'
return {'nav_pointer': nav_pointer}

(Não se esqueça de adicionar seu processador personalizado a TEMPLATE_CONTEXT_PROCESSORS em settings.py.)

Então, no modelo básico, uso uma tag ifequal por link para determinar se devo acrescentar a classe "ativa". Concedido, essa abordagem é estritamente limitada à flexibilidade de sua estrutura de caminho, mas funciona para minha implantação relativamente modesta.


Eu acho que realmente faz sentido ter isso no contexto global, para que você possa fazer referência à seção do site de várias maneiras (usando diferentes modelos para diferentes seções do site, por exemplo. +1.
shacker

2

Eu só queria compartilhar minha pequena melhoria na postagem de nivhab. Em meu aplicativo, tenho subnavigações e não queria ocultá-las usando apenas CSS, portanto, precisava de algum tipo de tag "if" para exibir ou não a subnavegação de um item.

from django import template
register = template.Library()

@register.tag
def ifnaviactive(parser, token):
    nodelist = parser.parse(('endifnaviactive',))
    parser.delete_first_token()

    import re
    args = token.split_contents()
    template_tag = args[0]
    if len(args) < 2:
        raise template.TemplateSyntaxError, "%r tag requires at least one argument" % template_tag
    return NavSelectedNode(args[1:], nodelist)

class NavSelectedNode(template.Node):
    def __init__(self, patterns, nodelist):
        self.patterns = patterns
        self.nodelist = nodelist

    def render(self, context):
        path = context['request'].path
        for p in self.patterns:
            pValue = template.Variable(p).resolve(context)
            if path == pValue:
                return self.nodelist.render(context)
        return ""

Você pode usar isso basicamente da mesma forma que a tag ativa:

{% url product_url as product %}

{% ifnaviactive request product %}
    <ul class="subnavi">
        <li>Subnavi item for product 1</li>
        ...
    </ul>
{% endifnaviactive %}

2

Apenas mais um aprimoramento da solução original.

Isso aceita vários padrões, e o que é melhor também é padrões sem nome escritos como URL relativo envolvido em '"', como a seguir:

{% url admin:clients_client_changelist as clients %}
{% url admin:clients_town_changelist as towns %}
{% url admin:clients_district_changelist as districts %}

<li class="{% active "/" %}"><a href="/">Home</a></li>
<li class="{% active clients %}"><a href="{{ clients }}">Clients</a></li>
{% if request.user.is_superuser %}
<li class="{% active towns districts %}">
    <a href="#">Settings</a>
    <ul>
        <li><a href="{{ towns }}">Towns</a></li>
        <li><a href="{{ districts }}">Districts</a></li>
    </ul>
</li>
{% endif %}

Tag funciona assim:

from django import template

register = template.Library()

@register.tag
def active(parser, token):
    args = token.split_contents()
    template_tag = args[0]
    if len(args) < 2:
        raise template.TemplateSyntaxError, "%r tag requires at least one argument" % template_tag
    return NavSelectedNode(args[1:])

class NavSelectedNode(template.Node):
    def __init__(self, urls):
        self.urls = urls

    def render(self, context):
        path = context['request'].path

        for url in self.urls:
            if '"' not in url:
                cpath = template.Variable(url).resolve(context)
            else:
                cpath = url.strip('"')

            if (cpath == '/' or cpath == '') and not (path == '/' or path == ''):
                return ""
            if path.startswith(cpath):
                return 'active'
        return ""

2

Usei jquery para destacar minhas barras de navegação. Esta solução simplesmente adiciona a classe css "ativa" ao item que se ajusta ao seletor css.

<script type="text/javascript" src="/static/js/jquery.js"></script>
<script>
    $(document).ready(function(){
        var path = location.pathname;
        $('ul.navbar a.nav[href$="' + path + '"]').addClass("active");
    });
</script>

2

Uma pequena melhoria em relação à resposta de @tback , sem quaisquer %if%tags:

# navigation.py
from django import template
from django.core.urlresolvers import resolve

register = template.Library()

@register.filter(name="activate_if_active", is_safe=True)
def activate_if_active(request, urlname):
  if resolve(request.get_full_path()).url_name == urlname:
    return "active"
  return ''

Use-o em seu modelo assim:

{% load navigation %}
<li class="{{ request|activate_if_active:'url_name' }}">
  <a href="{% url 'url_name' %}">My View</a>
</li>

E inclua "django.core.context_processors.request"em sua TEMPLATE_CONTEXT_PROCESSORSconfiguração.


2

Achei que o melhor é usar uma tag de inclusão:

templates/fnf/nav_item.html

<li class="nav-item">
    <a class="nav-link {% if is_active %}active{% endif %}" href="{% url url_name %}">{{ link_name }}</a>
</li>

Este é apenas o meu item de navegação básico de boot que desejo renderizar.

Ele obtém o valor href e, opcionalmente, o valor link_name. is_activeé calculado com base na solicitação atual.

templatetags/nav.py

from django import template

register = template.Library()


@register.inclusion_tag('fnf/nav_item.html', takes_context=True)
def nav_item(context, url_name, link_name=None):
    return {
        'url_name': url_name,
        'link_name': link_name or url_name.title(),
        'is_active': context.request.resolver_match.url_name == url_name,
    }

Em seguida, use-o em um nav: templates/fnf/nav.html

{% load nav %}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
        <ul class="navbar-nav mr-auto">
                {% nav_item 'dashboard' %}
            </ul>

É apenas uma leitura superficial, mas isso não limita as coisas a correspondências exatas no URL? Eu costumo usar dicas de navegação como esta para páginas profundas também. Por exemplo, o item de navegação Sobre seria destacado se você estivesse em /about/company-history/ou /about/what-we-do/
Oli

1
Sim, mas is_activepodem ser substituídas e outras chaves adicionadas ao dicionário retornadas. Além disso, o cheque pode ser context.request.resolver_match.url_name.startswith(x)ou qualquer outra coisa. Além disso, você pode ter código antes da instrução return para estabelecer os valores de dict. Além disso, você pode usar modelos diferentes, ou seja, um para top_level_nav.htmlcom lógica diferente, etc.
Tjorriemorrie

Solução limpa e simples ... legal!
mmw

1

Modificando ligeiramente a resposta de Andreas, parece que você pode passar o nome da rota de urls.py para a tag do modelo. No meu exemplo my_tasks, e na função de tag de modelo, use a função reversa para descobrir qual deve ser a URL, então você pode compará-la com a URL no objeto de solicitação (disponível no contexto do modelo)

from django import template
from django.core.urlresolvers import reverse

register = template.Library()

@register.tag
def active(parser, token):
    args = token.split_contents()
    template_tag = args[0]
    if len(args) < 2:
        raise template.TemplateSyntaxError, "%r tag requires at least one argument" % template_tag
    return NavSelectedNode(args[1:])

class NavSelectedNode(template.Node):
    def __init__(self, name):
        self.name = name

    def render(self, context):

        if context['request'].path == reverse(self.name[1]):
            return 'active'
        else:
            return ''

urls.py

url(r'^tasks/my', my_tasks, name = 'my_tasks' ),

template.html

<li class="{% active request all_tasks %}"><a href="{% url all_tasks %}">Everyone</a></li>

Talvez, uma abordagem mais direta: turnkeylinux.org/blog/django-navbar
jgsogo

1

Eu sei que estou atrasado para a festa. Não gostei de nenhuma das soluções populares:

O método de bloqueio parece errado: acho que a navegação deve ser independente.

O método template_tag parece errado: eu não gosto de ter que obter o url da url-tag primeiro. Além disso, acho que a classe css deve ser definida no modelo, não na tag.

Portanto, escrevi um filtro que não tem as desvantagens que descrevi acima. Ele retorna Truese um url está ativo e, portanto, pode ser usado com {% if %}:

{% load navigation %}
<li{% if request|active:"home" %} class="active"{% endif %}><a href="{% url "home" %}">Home</a></li>

O código:

@register.filter(name="active")
def active(request, url_name):
    return resolve(request.path_info).url_name == url_name

Apenas certifique-se de usar RequestContextem páginas com navegação ou de habilitar a solicitação context_processor em seusettings.py

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    'django.core.context_processors.request',
)

1

Eu vi jpwatts ' 110.º-J ' s, nivhab 's & Marcus Whybrow ' s respostas, mas todos eles parecem falta em alguma coisa: o que acontece com o caminho da raiz? Por que está sempre ativo?

Então criei uma outra forma, mais fácil, que faz o "controlador" decidir por si mesmo e acho que resolve a maioria dos grandes problemas.

Aqui está minha tag personalizada:

## myapp_tags.py

@register.simple_tag
def nav_css_class(page_class):
    if not page_class:
        return ""
    else:
        return page_class

Então, o "controlador" declara as classes CSS necessárias (na verdade, o mais importante é que declara sua presença ao template)

## views.py

def ping(request):
    context={}
    context["nav_ping"] = "active"
    return render(request, 'myapp/ping.html',context)

E, finalmente, eu o renderizo na minha barra de navegação:

<!-- sidebar.html -->

{% load myapp_tags %}
...

<a class="{% nav_css_class nav_home %}" href="{% url 'index' %}">
    Accueil
</a>
<a class="{% nav_css_class nav_candidats %}" href="{% url 'candidats' %}">
    Candidats
</a>
<a class="{% nav_css_class nav_ping %}" href="{% url 'ping' %}">
    Ping
</a>
<a class="{% nav_css_class nav_stat %}" href="{% url 'statistiques' %}">
    Statistiques
</a>
...

Portanto, cada página tem seu próprio nav_css_classvalor a ser definido e, se for definido, o modelo torna-se ativo: sem necessidade de requestno contexto do modelo, sem pareamento de URL e sem mais problemas com páginas multi-URL ou página raiz.


1

Inspirado por esta solução , comecei a usar esta abordagem:

**Placed in templates as base.html**

{% block tab_menu %}
<ul class="tab-menu">
  <li class="{% if active_tab == 'tab1' %} active{% endif %}"><a href="#">Tab 1</a></li>
  <li class="{% if active_tab == 'tab2' %} active{% endif %}"><a href="#">Tab 2</a></li>
  <li class="{% if active_tab == 'tab3' %} active{% endif %}"><a href="#">Tab 3</a></li>
</ul>
{% endblock tab_menu %}

**Placed in your page template**

{% extends "base.html" %}

{% block tab_menu %}
  {% with active_tab="tab1" %} {{ block.super }} {% endwith %}
{% endblock tab_menu %}

0

Aqui está minha chance. Acabei implementando uma classe em minhas visualizações que contém minha estrutura de navegação (plana com alguns metadados). Em seguida, injeto isso no modelo e o renderizo.

Minha solução lida com i18n. Provavelmente deveria ser abstraído um pouco mais, mas eu realmente não me preocupei com isso.

views.py:

from django.utils.translation import get_language, ugettext as _


class Navi(list):
    items = (_('Events'), _('Users'), )

    def __init__(self, cur_path):
        lang = get_language()
        first_part = '/' + cur_path.lstrip('/').split('/')[0]

        def set_status(n):
            if n['url'] == first_part:
                n['status'] == 'active'

        for i in self.items:
            o = {'name': i, 'url': '/' + slugify(i)}
            set_status(o)
            self.append(o)

# remember to attach Navi() to your template context!
# ie. 'navi': Navi(request.path)

Eu defini a lógica do template usando inclui como este. Modelo base:

{% include "includes/navigation.html" with items=navi %}

Inclusão real (inclui / navegação.html):

 <ul class="nav">
     {% for item in items %}
         <li class="{{ item.status }}">
             <a href="{{ item.url }}">{{ item.name }}</a>
         </li>
     {% endfor %}
 </ul>

Esperançosamente, alguém achará isso útil! Acho que seria muito fácil estender essa ideia para oferecer suporte a hierarquias aninhadas, etc.


0

Crie um modelo de inclusão "intranet / nav_item.html":

{% load url from future %}

{% url view as view_url %}
<li class="nav-item{% ifequal view_url request.path %} current{% endifequal %}">
    <a href="{{ view_url }}">{{ title }}</a>
</li>

E inclua-o no elemento nav:

<ul>
    {% include "intranet/nav_item.html" with view='intranet.views.home' title='Home' %}
    {% include "intranet/nav_item.html" with view='crm.views.clients' title='Clients' %}
</ul>

E você precisa adicionar isso às configurações:

from django.conf import global_settings
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
    'django.core.context_processors.request',
)


0

desta pergunta SO

{% url 'some_urlpattern_name' as url %}
<a href="{{url}}"{% if request.path == url %} class="active"{% endif %}>Link</a>

Repita conforme necessário para cada link.


Isso só funciona para correspondências diretas. A maioria dos sistemas de navegação marca o item de navegação ativo se uma página descendente também estiver ativa. Ou seja, se /blog/posts/2021/04/12fosse o url, o item / blog / nav estaria ativo.
Oli,

@Oli sim não vai funcionar algumas vezes. por exemplo em stackoverflow ou seja, navegação Questions, Tags, Users, Badges, Unanswered, Ask Question. não funcionará para Questions, mas para todos os outros navs funcionará bem.
suhailvs

0

Eu também usei o jQuery para destacá-lo e considerá-lo mais elegante do que bagunçar o template com tags de template Django não-semânticas.

O código abaixo funciona com menus suspensos aninhados no bootstrap 3 (destaca o elemento pai e o <li>elemento filho .

// DOM Ready
$(function() {
    // Highlight current page in nav bar
    $('.nav, .navbar-nav li').each(function() {
        // Count the number of links to the current page in the <li>
        var matched_links = $(this).find('a[href]').filter(function() {
            return $(this).attr('href') == window.location.pathname; 
        }).length;
        // If there's at least one, mark the <li> as active
        if (matched_links)
            $(this).addClass('active');
    });
});

Também é muito fácil adicionar um clickevento return false(ou alterar o hrefatributo para #) para a página atual, sem alterar a marcação de template / html:

        var matched_links = $(this).find('a[href]').filter(function() {
            var matched = $(this).attr('href') == window.location.pathname;
            if (matched)
                $(this).click(function() { return false; });
            return matched;
        }).length;

0

Eu uso uma combinação deste mixin para visualizações baseadas em classe:

class SetActiveViewMixin(object):
    def get_context_data(self, **kwargs):
        context = super(SetActiveViewMixin, self).get_context_data(**kwargs)
        context['active_nav_menu'] = {
            self.request.resolver_match.view_name: ' class="pure-menu-selected"'
        }
        return context

com isso no modelo:

<ul>
    <li{{active_nav_menu.node_explorer }}><a href="{% url 'node_explorer' '' %}">Explore</a></li>
    <li{{active_nav_menu.node_create }}><a href="{% url 'node_create' path %}">Create</a></li>
    <li{{active_nav_menu.node_edit }}><a href="{% url 'node_edit' path %}">Edit</a></li>
    <li{{active_nav_menu.node_delete }}><a href="{% url 'node_delete' path %}">Delete</a></li>
</ul>

0

O meu é um pouco semelhante a outra abordagem JS enviada anteriormente .. só sem jQuery ...

Digamos que temos em base.html o seguinte:

<div class="pure-u-1 pure-menu pure-menu-open pure-menu-horizontal header" >
    <ul class="">
        <li id="home"><a href="{% url 'article:index' %}">Home</a></li>
        <li id="news"><a href="{% url 'article:index' %}">News</a></li>
        <li id="analysis"><a href="{% url 'article:index' %}">Analysis</a></li>
        <li id="opinion"><a href="{% url 'article:index' %}">Opinion</a></li>
        <li id="data"><a href="{% url 'article:index' %}">Data</a></li>
        <li id="events"><a href="{% url 'article:index' %}">Events</a></li>
        <li id="forum"><a href="{% url 'article:index' %}">Forum</a></li>
        <li id="subscribe"><a href="{% url 'article:index' %}">Subscribe</a></li>
    </ul>
    <script type="text/javascript">
        (function(){
            loc=/\w+/.exec(window.location.pathname)[0];
            el=document.getElementById(loc).className='pure-menu-selected';         
        })();   
    </script>
</div>

Acabei de fazer minha hierarquia para seguir um determinado padrão de URL ... após o endereço do host ... eu tenho minha categoria principal, por exemplo, casa, notícias, análise, etc. e a regex apenas puxa a primeira palavra do local

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.