Навигация в django

Я только что сделал свое первое маленькое веб-приложение на 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.12.2013 20:19

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

Evgeny Bobkin 03.01.2018 16:01

Может эта сетка поможет: djangopackages.org/grids/g/navigation

guettli 04.03.2020 00:31
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
111
3
85 900
30
Перейти к ответу Данный вопрос помечен как решенный

Ответы 30

Вы можете использовать обратная функция с соответствующими параметрами, чтобы получить текущий URL.

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

HTML:

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

CSS:

body.home #nav_home,
body.about #nav_about { */ Current nav styles */ }
Ответ принят как подходящий

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

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

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

anonymous coward 22.09.2009 18:47

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

shacker 20.02.2010 09:32

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

mlissner 06.07.2011 05:54

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

None-da 01.09.2011 20:37

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

jpwatts 04.09.2011 04:04

Это не работает на последней стабильной версии django, я получаю следующую ошибку: 'url' requires a non-empty first argument. The syntax changed in Django 1.5, see the docs.

pyCthon 27.07.2014 07:33

На первый взгляд это кажется интересным подходом, однако, если вы думаете о нескольких страницах, созданных на основе этого шаблона "base.html", этот подход безумный. Представьте, что вы решили изменить, расширить или удалить какой-либо из пунктов меню, тогда это означает, что вам нужно продумать каждую страницу, которая была расширена, и применить изменение везде, это просто противоречит всем парадигмам, лол

Evgeny Bobkin 03.01.2018 15:16

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

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

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

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

Justin Voss 06.12.2008 01:39

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

Oli 10.12.2008 16:09

Я так делаю:

<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-адреса, но оно работает для однократного взлома.

Matt Garrison 22.01.2010 02:20

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

justin 14.06.2010 15:48

Для этого вам не нужно 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

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

bsk 24.09.2010 01:31

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

Keyo 19.01.2011 10:00

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

Josh Smeaton 23.03.2011 01:38

Что мне не нравится в этом, так это то, что вы устанавливаете переменные с помощью тега url. Вместо этого более чистым решением может быть изменение URL-адреса в простом теге.

shawnjan 17.03.2012 02:52

Не забудьте добавить django.core.context_processors.request к вашему TEMPLATE_CONTEXT_PROCESSORS в settings.py

amigcamel 26.09.2014 09:23

почему import re находится внутри определения тега, а не на уровне модуля>

Nikolai Golub 09.11.2014 20:17

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

nerdwaller 14.03.2015 04:14

Мне понравилась чистота описанного выше 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 15.06.2015 19:06

У меня есть несколько меню на одной странице, которые создаются динамически с помощью цикла. Посты выше, касающиеся контекста, дали мне быстрое решение. Надеюсь, это кому-то поможет. (Я использую это в дополнение к активному тегу шаблона - мое исправление решает динамическую проблему). Это кажется глупым сравнением, но оно работает. Я решил назвать переменные 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>

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

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 26.02.2012 10:36

Я просто хотел поделиться своим незначительным улучшением в посте 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 %}

Я взял код из nivhab выше, удалил некоторые странности и превратил его в чистый тег шаблона, изменив его так, чтобы / 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 %}

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

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

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

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

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

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

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

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

Это принимает несколько шаблонов, и лучше всего также безымянные шаблоны, записанные как относительный 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 ""

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

Мое решение касается 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>

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

Немного изменив ответ Андреаса, похоже, вы можете передать имя маршрута из 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 11.07.2012 19:44

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

Я использовал 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>

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

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

Andrew Barber 21.02.2013 10:13

Я автор 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

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

CpILL 02.05.2018 06:20

Поскольку Django 1.5:

In all generic class-based views (or any class-based view inheriting from ContextMixin), the context dictionary contains a view variable that points to the View instance.

Поэтому, если вы используете такие представления, вы можете добавить что-нибудь вроде 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?

Oli 26.09.2013 13:31

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

Konrad Hałas 26.09.2013 13:58

@Oli улучшенный пример.

Konrad Hałas 26.09.2013 14:04

из этого ТАК вопрос

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

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

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

Oli 28.10.2013 17:59

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

suhailvs 28.10.2013 18:12

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

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

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

Я также использовал 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;

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

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>

Мой немного похож на другой подход 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 ... после адреса хоста ... у меня есть моя основная категория, например, дом, новости, анализ и т.д., а регулярное выражение просто вытягивает первое слово из местоположения

Небольшое улучшение по сравнению с ответом @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.

Я видел ответы jpwatts ', 110j, нивхаб и Маркус Уайброу, но всем им, кажется, чего-то не хватает: а как насчет корневого пути? Почему он всегда активен?

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

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

## 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-адресами или корневой страницей.

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

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

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

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 22.03.2019 13:39

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

Tjorriemorrie 24.03.2019 05:20

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

mmw 12.04.2019 16:55

**

Просто добавьте URL-адрес и имя в формате jinja, как это

**

 <ul class = "nav navbar-nav">
        <li>
           <a href = "{% url 'index' %}">Cities</a>
        </li>
        <li>
           <a href = "{% url 'all_cafes' %}">Cafes</a>
        </li>
    </ul>

Другие вопросы по теме