การนำทางใน django


104

ฉันเพิ่งทำ webapp ตัวแรกใน 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เป็นอาร์กิวเมนต์ที่สองที่จะแท็ก) ต่อท้ายแอตทริบิวต์ระดับที่มีอยู่ แต่ส่วนใหญ่เชื่อมโยง nav ว่าตัวอย่างคือสิ่งที่ผมใช้
orokusaki

คำถามนี้ดูเหมือนจะเกี่ยวข้องกับสิ่งนี้stackoverflow.com/a/9801473/5739875
Evgeny Bobkin

บางทีตารางนี้อาจช่วยได้: 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
ฉันไม่กระตือรือร้นเกี่ยวกับแนวทางนี้เพราะไม่ใช่เรื่องแปลกที่จะมีหลายส่วนของไซต์ที่จัดการโดยเทมเพลตย่อยเดียวกัน ดังนั้นคุณต้องวาง vars ที่กำหนดเองไว้ในมุมมองและเงื่อนไขในเทมเพลตหรือจัดเรียงเทมเพลตย่อยใหม่เพื่อให้มีลักษณะเฉพาะทั้งหมด ... ทั้งหมดเพียงเพื่อตรวจจับส่วนไซต์ปัจจุบัน วิธีการแท็กแม่แบบจะสะอาดขึ้นในที่สุด
shacker

ฉันดูวิธีแก้ปัญหาอื่น ๆ เล็กน้อยและดูเหมือนว่าพวกเขาทั้งหมดจะแฮ็ค อย่างน้อยอันนี้ค่อนข้างตรงไปตรงมาและใช้งานง่าย / เรื่องที่สนใจ
mlissner

ฉันปรับโครงสร้างใหม่<ul id="nav">....</ul>เป็นไฟล์อื่นสมมติว่า tabs.html ตอนนี้ base.html มีอยู่{%block nav%}{%include "tabs.html"%}{%endblock%}และการไฮไลต์ของแท็บที่ใช้งานอยู่หยุดทำงาน (ใน about.html ด้านบน) ฉันขาดอะไรไปหรือเปล่า?
ไม่มี - ดา

@ คุณพ่อคุณมีทิศทางเพียงพอที่จะเกิดขึ้นซึ่งฉันไม่แน่ใจว่าฉันจะเก็บมันไว้ในหัว แต่ฉันคิดว่าคำตอบเกี่ยวข้องกับวิธีการ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"> บ้าน </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
Keyo

3
@Keyo สร้างไดเรกทอรีเทมเพลตในโครงการของคุณและเพิ่มโครงการของคุณไปยังแอปที่ติดตั้ง นั่นจะเป็นเคล็ดลับ หรืออีกวิธีหนึ่งเช่นที่คุณกล่าวคือสร้างไซต์หลักของคุณเป็นแอปภายในโครงการของคุณ
Josh Smeaton

5
อย่าลืมเพิ่มdjango.core.context_processors.requestเข้าไปTEMPLATE_CONTEXT_PROCESSORSในsettings.py
amigcamel

1
สิ่งนี้ไม่ถูกต้องสำหรับรัฐที่อาจซ้อนกันเช่นmysite.com(เป็นบ้าน) และmysite.com/blogเนื่องจากเส้นทางจะแสดงเป็น/และ/blog/(ตามลำดับ) ที่ให้ผลการแข่งขันสำหรับอดีตในแต่ละครั้ง หากคุณไม่ได้ใช้/เป็นที่จอดรถอาจไม่เป็นไรอย่างอื่นฉันก็ใช้return 'active' if pattern == request.path else ''(ฉันยังไม่พบปัญหาเกี่ยวกับสิ่งนี้ แต่ฉันเพิ่งตั้งค่าโดยใช้สิ่งนี้)
nerdwaller

33

ฉันชอบความสะอาดของ 110j ข้างต้นดังนั้นฉันจึงใช้มันส่วนใหญ่และปรับโครงสร้างใหม่เพื่อแก้ปัญหา 3 ข้อที่ฉันมี:

  1. นิพจน์ทั่วไปตรงกับ url "home" กับอื่น ๆ ทั้งหมด
  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>

บางทีเราอาจจะตอบได้ดีที่สุดกับ Marcus one แต่มันทำงานอย่างไรกับ "บ้าน"? มันทำงานอยู่เสมอ? จะทำให้ใช้งานได้เฉพาะในการเรียก URL ราก (www.toto.com/ และ www.toto.com/index) คำตอบทั้งสองไม่ทำให้เกิดปัญหานี้ ...
DestyNova

20

ฉันเป็นผู้เขียนdjango-lineageซึ่งฉันเขียนขึ้นเพื่อแก้ปัญหานี้โดยเฉพาะ: D

ฉันรู้สึกรำคาญเมื่อใช้วิธี jpwatts (ที่ยอมรับได้อย่างสมบูรณ์แบบ) ในโครงการของฉันเองและได้รับแรงบันดาลใจจากคำตอบของ 110j Lineage มีลักษณะดังนี้:

{% 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 จะถูกแทนที่ด้วย "active" หากอาร์กิวเมนต์ตรงกับจุดเริ่มต้นของ URL ของหน้าปัจจุบัน

{% url %}นอกจากนี้ยังรองรับอาร์กิวเมนต์ตัวแปรและความละเอียดย้อนกลับแบบเต็ม ฉันโรยตัวเลือกการกำหนดค่าบางอย่างและทำให้เนื้อออกมาเล็กน้อยและบรรจุขึ้นเพื่อให้ทุกคนใช้

หากใครสนใจอ่านรายละเอียดเพิ่มเติมได้ที่:

>> github.com/marcuswhybrow/django-lineage


1
เส้นทางการเข้ารหัสที่ยากลงในเทมเพลต :(
CpILL

10

ตั้งแต่Django 1.5 :

ในมุมมองตามคลาสทั่วไปทั้งหมด (หรือมุมมองตามคลาสใด ๆ ที่สืบทอดจาก ContextMixin) พจนานุกรมบริบทมีตัวแปรมุมมองที่ชี้ไปยังอินสแตนซ์ View

ดังนั้นหากคุณกำลังใช้มุมมองดังกล่าวคุณสามารถเพิ่มสิ่งที่ชอบ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

ใช่ แต่โดยทั่วไปแล้วนี่คือสิ่งที่คุณต้องการบรรลุด้วยการนำทางหลายระดับ แน่นอนคุณสามารถใส่หนึ่งรายการได้breadcrumbsหากต้องการ แต่คุณมีสิทธิ์ - ตัวอย่างของฉันไม่ใช่ตัวอย่างที่ดีที่สุด
Konrad Hałas

@Oli ปรับปรุงตัวอย่าง
Konrad Hałas

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 แบบฮาร์ดโค้ด แต่ใช้ได้กับการแฮ็กครั้งเดียว
Matt Garrison

ปัญหาในการแก้ปัญหานี้คือคุณมี "สถิติ" แบบฮาร์ดโค้ดลงในโค้ด สิ่งนี้เอาชนะวัตถุประสงค์ของการใช้แท็ก url เพื่อรับ url ของหน้า
justin

7

ฉันใช้รหัสจาก 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 %}

6

นี่เป็นเพียงตัวแปรของโซลูชัน css ที่เสนอโดย Toba ด้านบน:

รวมสิ่งต่อไปนี้ไว้ในเทมเพลตพื้นฐานของคุณ:

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

จากนั้นในเทมเพลตของคุณที่ขยายการใช้งานพื้นฐาน:

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

จากนั้นคุณสามารถใช้ css เพื่อเน้นพื้นที่ปัจจุบันโดยใช้แท็ก body (ตัวอย่างเช่นหากเรามีลิงก์ที่มี id ของ 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 ดิบในมุมมองของคุณซึ่งเป็นสิ่งที่ต้องใช้เทคนิคนี้ คุณคิดเกี่ยวกับการเขียนแท็กเทมเพลตที่กำหนดเองหรือไม่?
Justin Voss

คุณถูก. ฉันได้แก้ไขเพื่อหยุดการส่งผ่าน HTML พึ่งผ่านทรูตอนนี้ ฉันยังไม่ได้เขียนแท็กเทมเพลตเลย แต่ใช่นี่อาจเป็นจุดเริ่มต้นที่ดี
Oli

2

ฉันมีหลายเมนูในเพจเดียวกันที่สร้างแบบไดนามิกผ่านการวนซ้ำ โพสต์ด้านบนที่เกี่ยวข้องกับบริบทช่วยให้ฉันแก้ไขได้อย่างรวดเร็ว หวังว่านี่จะช่วยใครสักคน (ฉันใช้สิ่งนี้นอกเหนือจากแท็กเทมเพลตที่ใช้งานอยู่ - การแก้ไขของฉันช่วยแก้ปัญหาไดนามิกได้) ดูเหมือนเป็นการเปรียบเทียบโง่ ๆ แต่ได้ผล ฉันเลือกที่จะตั้งชื่อตัวแปร active_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 ต่อลิงก์เพื่อพิจารณาว่าจะต่อท้ายคลาส "active" หรือไม่ จริงอยู่ที่วิธีนี้ จำกัด เฉพาะความยืดหยุ่นของโครงสร้างเส้นทางของคุณ แต่ใช้ได้กับการปรับใช้ที่ค่อนข้างเรียบง่ายของฉัน


ฉันคิดว่ามันสมเหตุสมผลจริงๆที่จะมีสิ่งเหล่านี้ในบริบททั่วโลกดังนั้นคุณสามารถอ้างอิงส่วนไซต์ได้หลายวิธี (โดยใช้เทมเพลตที่แตกต่างกันสำหรับส่วนต่างๆของไซต์เช่น +1
shacker

2

ฉันแค่อยากแบ่งปันการปรับปรุงเล็กน้อยของฉันในโพสต์ของ nivhab ในแอปพลิเคชันของฉันฉันมีการนำทางย่อยและฉันไม่ต้องการซ่อนโดยใช้ CSS เพียงอย่างเดียวดังนั้นฉันจึงต้องการแท็ก "if" เพื่อแสดงการนำทางย่อยสำหรับรายการหรือไม่

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 "active" ให้กับรายการที่เหมาะกับตัวเลือก 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>

นี่เป็นเพียงรายการนำทาง bootstrap พื้นฐานของฉันที่ฉันต้องการแสดงผล

ได้รับค่า 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

แก้ไขคำตอบของ Andreas เล็กน้อยดูเหมือนว่าคุณจะส่งชื่อเส้นทางจาก 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-class ในเทมเพลตไม่ใช่แท็ก

ฉันจึงเขียนตัวกรองที่ไม่มีข้อบกพร่องตามที่อธิบายไว้ข้างต้น จะส่งคืน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และ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
โปรดทราบว่าคุณควรโพสต์จุดที่มีประโยชน์ของคำตอบที่นี่ในเว็บไซต์นี้หรือโพสต์ของคุณมีความเสี่ยงที่จะถูกลบ"ไม่ใช่คำตอบ" คุณยังสามารถใส่ลิงก์ได้หากต้องการ แต่เป็นเพียง "ข้อมูลอ้างอิง" เท่านั้น คำตอบควรอยู่ในตัวเองโดยไม่ต้องใช้ลิงก์
Andrew Barber

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

@ โอลี่ใช่มันจะไม่ทำงานบางครั้ง ยกตัวอย่างเช่นใน IE นำทาง StackOverflow Questions, Tags, Users, Badges, ,Unanswered Ask Questionมันใช้ไม่ได้Questionsแต่สำหรับการนำทางอื่น ๆ ทั้งหมดมันจะทำงานได้ดี
suhailvs

0

ฉันยังใช้ jQuery เพื่อเน้นและพบว่ามันดูหรูหรากว่าการทำให้เทมเพลตยุ่งเหยิงด้วยแท็กเทมเพลต Django ที่ไม่ใช่ความหมาย

โค้ดด้านล่างทำงานร่วมกับดรอปดาวน์ที่ซ้อนกันใน bootstrap 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แอตทริบิวต์เป็น#) สำหรับหน้าปัจจุบันโดยไม่ต้องเปลี่ยนเทมเพลต / มาร์กอัป 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 บางอย่าง ... หลังจากที่อยู่โฮสต์ ... ฉันมีหมวดหมู่หลักของฉันเช่นบ้านข่าวการวิเคราะห์ ฯลฯ และ regex ก็ดึงคำแรกออกจากตำแหน่ง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.