Навигация в django


104

Я только что сделал свое первое маленькое веб-приложение на django, и мне это нравится. Я собираюсь начать преобразование старого производственного PHP-сайта в django, и как часть его шаблона есть панель навигации.

В PHP я сверяю URL-адрес каждой опции навигации с текущим URL-адресом в коде шаблона и применяю класс CSS, если они совпадают. Это ужасно грязно.

Есть ли что-нибудь получше для django или хороший способ обработки кода в шаблоне?

Для начала, как мне получить текущий URL?


Для этого я создал github.com/orokusaki/django-active-menu - он поддерживает вложенные структуры URL-адресов и полагается на конфигурацию, а не на соглашение (как бы злобно это ни звучало), поэтому вы можете определять иерархию своего сайта, как хотите. Вы просто пользуетесь <a href="{% url "view:name" %}" {% active_class "view:name" %}>. При желании вы можете использовать его для генерации только на " active"значении (путем передачи в Falseкачестве второго аргумента тега) для добавления к существующему атрибуту класса, но для большинства навигационных ссылок, например , что я использую.
orokusaki 07

Этот вопрос, кажется, связан с этим stackoverflow.com/a/9801473/5739875
Евгений Бобкин

Может быть, эта сетка поможет: djangopackages.org/grids/g/navigation
guettli

Ответы:


74

Я использую наследование шаблонов для настройки навигации. Например:

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

Мне очень нравится эта идея, особенно из-за гибкости, но она предполагает меньшее количество СУХОЙ компромисс. Однако я начал использовать это на сайте.
анонимный трус

23
Я не в восторге от этого подхода, потому что нередко несколько разделов сайта обрабатываются одним и тем же подшаблоном. Таким образом, вы в конечном итоге помещаете пользовательские переменные в представления и условные выражения в шаблонах или переупорядочиваете подшаблоны, чтобы они все были уникальными ... и все просто для обнаружения текущего раздела сайта. Подход с использованием шаблонных тегов в конечном итоге становится чище.
shacker

Я посмотрел на несколько других решений, и мне кажется, что все они немного взломаны. Этот, по крайней мере, довольно прост и прост в реализации / утилизации.
mlissner 06

Я <ul id="nav">....</ul>изменил код на другой файл, скажем tabs.html. Итак, теперь base.html содержится, {%block nav%}{%include "tabs.html"%}{%endblock%}а затем выделение активной вкладки перестало работать (в about.html выше). Я что-нибудь упускаю?
None-da

@Maddy У вас достаточно косвенных действий, и я не совсем уверен, что держу это прямо в голове, но я думаю, что ответ связан с тем, как includeработает тег. Ознакомьтесь с примечанием, включенным в документы: docs.djangoproject.com/en/dev/ref/templates/builtins/#include В вашем случае, когда вы пытаетесь переопределить базовый шаблон about.html, я думаю, вы уже получил обработанный блок HTML, а не блок шаблона Django, ожидающий обработки.
jpwatts

117

Для этого вам не нужно if, взгляните на следующий код:

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>

Это оно. подробности реализации можно найти на сайте:
gnuvince.wordpress.com
110j.wordpress.com


2
В свойствах href отсутствуют скобки шаблона django {{,}}. Например, <a class="{% active request home %}" href="home"> Home </a> должен иметь вид <a class = "{% active request home%}" href = "{{home} } "> На главную </a> файл tags.py также потребует нескольких включений. В остальном отличное решение!
bsk

2
+1 Это более слабо связано с приложениями. Как новичок я понял, что для тегов нужно собственное приложение, вы не можете просто выгрузить его в глобальный файл tags.py. Я создал новое приложение под названием «Теги», и все прошло гладко. docs.djangoproject.com/en/dev/howto/custom-template-tags
Кейо,

3
@Keyo, создайте каталог templatetags в своем проекте и добавьте свой проект в installedapps. Это тоже поможет. В качестве альтернативы, как вы сказали, создайте свой основной сайт как приложение в своем проекте.
Джош Смитон,

5
Не забудьте добавить django.core.context_processors.requestв свой TEMPLATE_CONTEXT_PROCESSORSinsettings.py
amigcamel

1
Это недопустимо для состояний, которые могут быть вложенными, например mysite.com(как домашний) и mysite.com/blog, поскольку путь будет отображаться как /и /blog/(соответственно), каждый раз давая совпадение для первого. Если вы не используете /в качестве лендинга, это может быть нормально, в противном случае я просто использую return 'active' if pattern == request.path else ''(я еще не видел проблем с этим, но я только что настроил его).
ботаник,

33

Мне понравилась чистота описанного выше 110j, поэтому я взял большую часть и переработал, чтобы решить 3 проблемы, которые у меня были с ним:

  1. регулярное выражение соответствовало "домашнему" URL-адресу всем остальным
  2. Мне нужно было несколько URL-адресов, сопоставленных с одной вкладкой навигации , поэтому мне нужен был более сложный тег, который принимает переменное количество параметров.
  3. исправлены некоторые проблемы с URL

Вот:

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>

Возможно, нам лучше всего ответить на вопрос Маркуса, но как это работает с «домом»? он всегда активен? Как сделать его активным только при вызове корневого URL (www.toto.com/ и www.toto.com/index)? Оба ответа не приводят к этой проблеме ...
DestyNova

20

Я автор django-lineage, который я написал специально для решения этого вопроса: D

Меня раздражало использование (вполне приемлемого) метода jpwatts в моих собственных проектах, и я черпал вдохновение из ответа 110j. Происхождение выглядит так:

{% 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 просто заменяется на «активный», если аргумент совпадает с началом URL-адреса текущей страницы.

{% url %}Также поддерживаются переменные аргументы и обратное разрешение полного типа. Я добавил несколько вариантов конфигурации, немного доработал и упаковал для всех.

Если кому-то интересно, читайте об этом подробнее на:

>> github.com/marcuswhybrow/django-lineage


1
пути жесткого кодирования в шаблон :(
CpILL

10

Начиная с Django 1.5 :

Во всех общих представлениях на основе классов (или любых представлениях на основе классов, унаследованных от ContextMixin) словарь контекста содержит переменную представления, которая указывает на экземпляр представления.

Поэтому, если вы используете такие представления, вы можете добавить что-нибудь похожее breadcrumbsна поле уровня класса и использовать его в своих шаблонах.

Пример кода просмотра:

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

В своем шаблоне вы можете использовать его так:

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

Если вы хотите дополнительно «выделить» родительские элементы навигации, вам необходимо расширить breadcrumbsсписок:

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

... и в вашем шаблоне:

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

Это простое и понятное решение, которое отлично работает с вложенной навигацией.


В этом примере не будут ли все три элемента навигации .active?
Оли

Да, но это обычно то, чего вы хотите достичь с помощью многоуровневой навигации. Вы, конечно, можете поместить туда один предмет, breadcrumbsесли хотите. Но вы правы - мой пример не лучший.
Конрад Галас,

@Oli улучшенный пример.
Конрад Галас,

9

Вы можете применить класс или идентификатор к основному элементу страницы, а не к конкретному элементу навигации.

HTML:

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

CSS:

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

8

Я так делаю:

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

а затем все, что мне нужно сделать, на мой взгляд, добавить {'active_tab': 'statistics'}в мой контекстный словарь.

Если вы используете, RequestContextвы можете получить текущий путь в своем шаблоне как:

{{ request.path }}

И на ваш взгляд:

from django.template import RequestContext

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

Спасибо, что поделились этой информацией. Я использовал этот метод, но у меня была плоская страница на панели навигации, поэтому, чтобы обнаружить это и правильно выделить, я использовал {% ifequal flatpage.url '/ about /'%}. Мне не нравится жестко запрограммированное определение URL-адреса, но оно работает для однократного взлома.
Мэтт Гаррисон,

Проблема с этим решением заключается в том, что вы жестко запрограммировали «статистику» в коде. Это противоречит цели использования тега url для получения URL-адреса страницы.
Джастин

7

Я взял код из нивхаба выше, удалил некоторые странности и превратил его в чистый тег шаблона, изменив его так, чтобы / account / edit / по-прежнему активировал / account / tab.

#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

Это всего лишь вариант решения css, предложенного Тоба выше:

Включите в свой базовый шаблон следующее:

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

Затем в ваших шаблонах, расширяющих базовое использование:

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

Затем вы можете использовать css для выделения текущей области на основе тега body (например, если у нас есть ссылка с идентификатором nav-home):

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


3

Спасибо за ваши ответы, господа. Я снова пошел за чем-то немного другим ...

В моем шаблоне:

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

Как только я определил, на какой странице я нахожусь в логике (обычно в urls.py), я передаю шаблону class="selected"как часть контекста под правильным именем.

Например, если я нахожусь на странице link1, я добавляю {'link1_active':' class="selected"'}контекст для шаблона, чтобы извлечь и внедрить.

Кажется, он работает, и он довольно чистый.

Изменить: чтобы HTML не попадал в мой контроллер / представление, я немного изменил это:

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

Это делает шаблон немного менее читаемым, но я согласен, лучше не проталкивать необработанный HTML из файла urls.


2
Вам действительно следует избегать обработки необработанного HTML в вашем представлении, чего требует этот метод. Вы думали о написании собственного тега шаблона?
Джастин Восс

Ты прав. Я отредактировал, чтобы прекратить прохождение HTML. Я сейчас просто прохожу через True. Я еще не написал никаких шаблонных тегов, но да, это может быть хорошим местом для начала.
Оли

2

У меня есть несколько меню на одной странице, которые создаются динамически с помощью цикла. Посты выше, касающиеся контекста, дали мне быстрое решение. Надеюсь, это кому-то поможет. (Я использую это в дополнение к активному тегу шаблона - мое исправление решает динамическую проблему). Это сравнение кажется глупым, но оно работает. Я решил назвать переменные active_something-unique и something-unique, таким образом это работает с вложенными меню.

Вот часть представления (достаточно, чтобы понять, что я делаю):

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),

    }

А это из шаблона:

<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

Мое решение заключалось в том, чтобы написать простой контекстный процессор для установки переменной на основе пути запроса:

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}

(Не забудьте добавить свой собственный процессор в TEMPLATE_CONTEXT_PROCESSORS в settings.py.)

Затем в базовом шаблоне я использую тег ifequal для каждой ссылки, чтобы определить, добавлять ли «активный» класс. Конечно, этот подход строго ограничен гибкостью вашей структуры путей, но он работает для моего относительно скромного развертывания.


Я думаю, что действительно имеет смысл иметь их в глобальном контексте, поэтому вы можете ссылаться на раздел сайта различными способами (например, используя разные шаблоны для разных разделов сайта. +1.
shacker

2

Я просто хотел поделиться своим незначительным улучшением в посте nivhab. В моем приложении у меня есть поднавигации, и я не хотел скрывать их, используя только CSS, поэтому мне нужен был какой-то тег «если» для отображения поднавигации для элемента или нет.

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

Вы можете использовать это в основном так же, как активный тег:

{% url product_url as product %}

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

2

Еще одно усовершенствование оригинального решения.

Это принимает несколько шаблонов, и лучше всего также безымянные шаблоны, записанные как относительный URL-адрес, заключенный в '"', например:

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

Тег выглядит так:

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

Я использовал jquery, чтобы выделить свои панели навигации. Это решение просто добавляет класс css «активный» к элементу, который соответствует селектору 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

Небольшое улучшение по сравнению с ответом @tback без каких-либо %if%тегов:

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

Используйте его в своем шаблоне так:

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

И включите "django.core.context_processors.request"в свой TEMPLATE_CONTEXT_PROCESSORSсеттинг.


2

Я обнаружил, что лучше всего использовать тег включения:

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>

Это просто мой основной элемент навигации начальной загрузки, который я хочу визуализировать.

Он получает значение href и, возможно, значение link_name. is_activeрассчитывается исходя из текущего запроса.

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

Затем используйте его в навигаторе: 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>

Просто беглое прочтение, но разве это не ограничивает точные совпадения в URL? Я обычно использую такие подсказки для навигации и для глубоких страниц. Например, пункт About nav был бы выделен, если бы вы были на /about/company-history/или /about/what-we-do/
Oli

1
Да, но is_activeможно заменить и другие ключи добавить в словарь. Также чек может быть context.request.resolver_match.url_name.startswith(x)или чем угодно. Кроме того, вы можете иметь код перед оператором return, чтобы установить значения dict. Кроме того, вы можете использовать разные шаблоны, например, один для top_level_nav.htmlс разной логикой и т. Д.
Tjorriemorrie

Чистое и простое решение ... приятно!
mmw

1

Немного изменив ответ Андреаса, похоже, вы можете передать имя маршрута из urls.py в тег шаблона. В моем примере my_tasks, а затем в функции тега шаблона используйте обратную функцию, чтобы определить, каким должен быть URL-адрес, затем вы можете сопоставить это с URL-адресом в объекте запроса (доступном в контексте шаблона)

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>

Может быть, более простой подход: turnkeylinux.org/blog/django-navbar
jgsogo

1

Я знаю, что опаздываю на вечеринку. Однако мне не понравилось ни одно из популярных решений:

Блочный метод кажется неправильным: Я думаю , что навигация должна быть самодостаточной.

Метод template_tag кажется неправильным: мне не нравится, что я должен сначала получить URL-адрес из URL-тега. Кроме того, я думаю, что css-класс должен быть определен в шаблоне, а не в теге.

Поэтому я написал фильтр, который не имеет описанных выше недостатков. Он возвращается, Trueесли URL-адрес активен и поэтому может использоваться с {% if %}:

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

Код:

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

Просто убедитесь, что используете его RequestContextна страницах с навигацией или включите запрос context_processor в вашемsettings.py

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

1

Я видел ответы jpwatts ', 110j ' s, nivhab 's и Marcus Whybrow , но всем им, кажется, чего-то не хватает: а как насчет корневого пути? Почему он всегда активен?

Итак, я сделал другой способ, более простой, который позволяет «контроллеру» решать сам, и я думаю, что он решает большинство больших проблем.

Вот мой собственный тег:

## myapp_tags.py

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

Затем «контроллер» объявляет необходимые классы CSS (на самом деле, самое главное, он заявляет о своем присутствии в шаблон)

## views.py

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

И, наконец, я визуализирую его на панели навигации:

<!-- 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>
...

Таким образом, каждая страница имеет собственное nav_css_classзначение, которое нужно установить, и если оно установлено, шаблон становится активным: нет необходимости requestв контексте шаблона, нет парсинга URL-адресов и больше нет проблем с страницами с несколькими URL-адресами или корневой страницей.


1

Вдохновленный этим решением , я начал использовать такой подход:

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

Вот моя попытка. В итоге я реализовал в своих представлениях класс, содержащий мою структуру навигации (плоский с некоторыми метаданными). Затем я вставляю это в шаблон и визуализирую.

Мое решение касается i18n. Вероятно, следует немного абстрагироваться, но я особо этим не беспокоился.

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)

Я определил логику шаблона, используя такие включения. Базовый шаблон:

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

Фактические включают (включает / navigation.html):

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

Надеюсь, кому-нибудь это пригодится! Думаю, было бы довольно легко расширить эту идею для поддержки вложенных иерархий и т. Д.


0

Создайте шаблон включения "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>

И включите его в элемент 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>

И вам нужно добавить это в настройки:

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

0

вот довольно простое решение, https://github.com/hellysmile/django-activeurl


1
Обратите внимание, что вам следует публиковать полезные моменты ответа здесь, на этом сайте, иначе ваш пост может быть удален как «Не ответ» . Вы все еще можете включить ссылку, если хотите, но только в качестве «ссылки». Ответ должен стоять сам по себе, без ссылки.
Эндрю Барбер

0

из этого SO Вопрос

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

При необходимости повторите для каждой ссылки.


Это работает только для прямых совпадений. Большинство навигационных систем помечают навигационный элемент активным, если активна и дочерняя страница. Т.е. если бы это /blog/posts/2021/04/12был URL, то элемент / blog / nav был бы активен.
Оли

@Oli да, иногда это не сработает. например , в StackOverflow навигации , т.е. Questions, Tags, Users, Badges, Unanswered, Ask Question. он не будет работать Questions, но для всех других навигационных систем он будет работать нормально.
suhailvs

0

Я также использовал jQuery, чтобы выделить его и найти более элегантным, чем загромождение шаблона несемантическими тегами шаблонов Django.

Приведенный ниже код работает с вложенными раскрывающимися списками в начальной загрузке 3 (выделяет как родительский, так и дочерний <li>элемент.

// 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');
    });
});

Также довольно легко добавить clickсобытие return false(или изменить hrefатрибут #) для текущей страницы, не изменяя разметку 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

Я использую комбинацию этого миксина для представлений на основе классов:

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

с этим в шаблоне:

<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

Мой немного похож на другой подход JS, представленный ранее ... только без jQuery ...

Допустим, у нас есть в base.html следующее:

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

Я просто сделал свою иерархию, чтобы следовать определенному шаблону URL ... после адреса хоста ... у меня есть моя основная категория, например, дом, новости, анализ и т.д., а регулярное выражение просто вытягивает первое слово из местоположения

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.