Dynamically generate a menu with links in django templates with context processors
1 A solution
Imagine going to base.html
and writing all the a href links yourself… and
if new urls appears – you go and add them to base.html
..
We know better…
Let's use context processors
for this task.
First I was trying to add url info into some kind of view, but ofc circular
import error
, because I am trying To import views from urls and urls from
views.
Tried using util functions.. Nope.
Finally found out about context processors
. With them you can pass ANY kind
of data to EACH template.
So for exmaple, createa context_processor.py
file. This can be the content:
from datetime import datetime from one.urls import urlpatterns def current_time(request): return {'current_time': datetime.now()} def get_urls(request): # print(type(urlpatterns)) # print(urlpatterns) return {'url_patterns': urlpatterns}
In settings.py/templates
array add this - 'context_processors.get_urls',
Then in templates can loop over them:
<ul> {% for url in url_patterns %} <li><a href="{% url url.name %}">{{url.name}}</a></li> {% endfor %} </ul>
Now the nav should be generated.
2 A bug in the solution and a fix for it
PROBLEM ARISES WHEN HAVING DETAIL VIEWS OF THINGS:
path('detail/<int:pk>/', OneDetailView.as_view(), name='one_detail'),
Such error:
django.urls.exceptions.NoReverseMatch: Reverse for 'one_detail' with no arguments not found. 1 pattern(s) tried: ['one/detail/(?P<pk>[0-9]+)/\\Z']
Basically it does not know what to write in url.name, since pk is not provided
{% for url in url_patterns %} <li><a href="{% url url.name %}">{{url.name}}</a></li> {% endfor %}
Ok you can actually fix it like this:
randompatterns = [ # function views path("", views.index, name="index"), path("function_view", views.function_view, name="function_view"), # class based views path("class_view/", ClassView.as_view(), name="class_view"), path("greeting_parent/", GreetingView.as_view(), name="greeting_parent"), path("greeting_child/", MorningGreetingView.as_view(), name="greeting_child"), path("greeting_hack/", GreetingView.as_view(greeting="G'day"), name="greeting_hack"), path("async/", AsyncView.as_view(), name="async"), listpatterns = [ path("one_list_view/", OneListView.as_view(), name="one_list_view"), ] detailpatterns = [ path('detail/<int:pk>/', OneDetailView.as_view(), name='one_detail'), ] urlpatterns = randompatterns + listpatterns + detailpatterns
And then:
def get_urls(request): # print(type(urlpatterns)) # print(urlpatterns) return {'url_patterns': randompatterns + listpatterns}