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}