This is Part 3 of Building a Dashboard with Django and Bootstrap:
- Part 1: Building a base Django project
- Part 2: Prepare for dynamic content
- Part 3: Handling navigation and the side menu
- Part 4: Deploy Django App to Azure
Inhaltsverzeichnis
Introduction
If you follow the first part of this blog topic, you have a running Django dashboard.
But, the content ist still static. Lets review the current state:

Prepare our Django project
Right now, the whole content of our Django project is provided by the dashboard template
dashboard/template/site/base.html
Looking at our web site, you will see the different side menu items. So, intentionally, our web site should display different pages. And each page should provide the dynamic content.
The final goal of this part is to change our web app, so that each side item navigates us to a different page. For this, we have to take care about two things:
- Navigation: how to we get to another page in our app
- Project Structure: where to place the required components for each page
Navigation usually is the process of getting from one page to another by clicking on a link.
So, we need to things:
- the source page, containing the link
- the destination page
- the link, pointing to the destination page
Let’s take a look into the site template with the side menu:

The corresponding code in the side template is
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo"> <div class="bg-white py-2 collapse-inner rounded"> <h6 class="collapse-header">Custom Components:</h6> <a class="collapse-item" href="buttons.html">Buttons</a> <a class="collapse-item" href="cards.html">Cards</a> </div> </div>
This is the result:
![]() | → | ![]() |
Linking to a html page is not possible, because Django does not work with html pages. Navigation in Django works with urls (in urls.py
) and views in (views.py
).
We must replace the html link tag (<a href="buttons.html"
>) with an Django-conform code. Read here for more details and the basics.
The idea behind the navigation is
Define the needed links | /buttons |
Give each link a name | “buttons” |
Define, which view to call for this link | components/buttons/views.py |
Tell Django, how to insert this link in a html page | <a href="{% url 'buttons' %}">Buttons</a> |
With this in mind, we change our site template for the side navigation (e. g. for the components menu):

But, if you save the template and try to view the web page, you will see this error:

We missed to tell Django, what to do when the corresponding link for this name is requested. We have to tell Django to use the view defined in buttons/views.py
to generate the resulting view/page.
So, change the global url mapping file dashboard/urls.py
import dashboard.apps.components.buttons.views as ButtonsViews import dashboard.apps.components.cards.views as CardsViews urlpatterns = [ path('', include('dashboard.apps.urls')), path('buttons', ButtonsViews.IndexView.as_view(), name='buttons'), path('cards', CardsViews.IndexView.as_view(), name='cards'), path('admin/', admin.site.urls), ]
dashboard/apps/components/buttons/views.py
from django.shortcuts import render from django.views import generic class IndexView(generic.TemplateView): template_name = 'buttons/base.html'
dashboard/apps/components/cards/views.py
from django.shortcuts import render from django.views import generic class IndexView(generic.TemplateView): template_name = 'cards/base.html'
dashboard/apps/components/cards/templates/cards/base.html
{% extends 'site/base.html' %} {% load static %} {% block content %} <h1 style="text-align: center">CARDS</h1> {% endblock content %}
dashboard/apps/components/cards/templates/buttons/base.html
{% extends 'site/base.html' %} {% load static %} {% block content %} <h1 style="text-align: center">BUTTONS</h1> {% endblock content %}
Save everything and view at the resulting page

What happens, from showing the page, clicking on the link until you see the final result:
- Django create the side menu item Cards
- with the corresponding link
localhost:9000/cards
- which will be the destination page, if you click on the link
- and finally, Django creates the desired page (through
view.py
)

Namespaces and structure
Now, think about the names, e. g. buttons and cards in our Components.
Your project is getting bigger and you plan to add an additional page info
with general information for both Components and Utilities menu.
So, you will have to additional files, for example
dashboard/apps/components/info/views.py
dashboard/apps/utilities/info/views.py
The corresponding url mapping (urls.py
) could look like this:
import dashboard.apps.components.info.views as ComponentInfoViews import dashboard.apps.utilities.info.views as UtilitiesInfoViews urlpatterns = [ path('', include('dashboard.apps.urls')), path('info', ComponentInfoViews.IndexView.as_view(), name='info'), path('info', UtilitiesInfoViews.IndexView.as_view(), name='info'),
Two pages with the same name (info
) in different views.py
!
Of course, we could choose different names and link (component_info
, utilities_info
), but this not a good programming style.
We will choose a more modern way of programming
- Spread the responsibility over separate, independent modules
- Name these modules with different names
What does this mean? We will have
- a separate module
frontend
, only responsible for the start page (frontend) - a separate module
components
, responsible for all components - a separate module
utilities
, responsible for all utilities - a separate module
pages
, responsible for all pages
Resulting folder structure and file content
File dashbard/urls.py
urlpatterns = [ path('', include('dashboard.apps.urls')), path('admin/', admin.site.urls), ]
File dashbard/apps/urls.py
from django.urls import path from django.urls.conf import include from dashboard.apps.frontend.views import IndexView app_name = 'app' urlpatterns = [ path('', IndexView.as_view(), name='index'), path('pages/', include('dashboard.apps.pages.urls')), path('components/', include('dashboard.apps.components.urls')), path('utilities/', include('dashboard.apps.utilities.urls')), ]
File dashbard/apps/components/urls.py
from django.urls import path import dashboard.apps.components.buttons.views as ButtonsViews import dashboard.apps.components.cards.views as CardsViews app_name = 'components' urlpatterns = [ path('', ButtonsViews.IndexView.as_view(), name='index'), path('buttons/', ButtonsViews.IndexView.as_view(), name='buttons'), path('cards/', CardsViews.IndexView.as_view(), name='cards'), ]
File dashbard/apps/utilities/urls.py
from django.urls import path import dashboard.apps.utilities.colors.views as ColorsViews import dashboard.apps.utilities.borders.views as BordersViews import dashboard.apps.utilities.animations.views as AnimationsViews import dashboard.apps.utilities.others.views as OthersViews app_name = 'utilities' urlpatterns = [ path('', BordersViews.IndexView.as_view(), name='index'), path('animations/', AnimationsViews.IndexView.as_view(), name='animations'), path('borders/', BordersViews.IndexView.as_view(), name='borders'), path('colors/', ColorsViews.IndexView.as_view(), name='colors'), path('others/', OthersViews.IndexView.as_view(), name='others'), ]
File dashbard/apps/pages/urls.py
from django.urls import path import dashboard.apps.pages.blank.views as BlankViews import dashboard.apps.pages.login.views as LoginViews import dashboard.apps.pages.pagenotfound.views as PageNotFoundViews import dashboard.apps.pages.password.views as PasswordViews import dashboard.apps.pages.register.views as RegisterViews import dashboard.apps.pages.charts.views as ChartsViews import dashboard.apps.pages.tables.views as TablesViews app_name = 'pages' urlpatterns = [ path('', ChartsViews.IndexView.as_view(), name='index'), path('blank', BlankViews.IndexView.as_view(), name='blank'), path('charts', ChartsViews.IndexView.as_view(), name='charts'), path('login', LoginViews.IndexView.as_view(), name='login'), path('pagenotfound', PageNotFoundViews.IndexView.as_view(), name='pagenotfound'), path('password', PasswordViews.IndexView.as_view(), name='password'), path('register', RegisterViews.IndexView.as_view(), name='register'), path('tables', TablesViews.IndexView.as_view(), name='tables'), ]
Let’s finally check the namespace structure:
$ find . -name urls.py ./dashboard/urls.py ./dashboard/apps/utilities/urls.py ./dashboard/apps/components/urls.py ./dashboard/apps/urls.py ./dashboard/apps/pages/urls.py
We create three levels for our namespaces:
Djane URL File | Namespace |
---|---|
./dashboard/urls.py | – |
./dashboard/apps/urls.py | app |
./dashboard/apps/utilities/urls.py | app:utilities |
These namespaces must be used in the template files, for example:
<a href="{% url 'app:components:buttons' %}" class="collapse-item" >Buttons</a> <a href="{% url 'app:components:cards' %}" class="collapse-item" >Cards</a>
<a class="collapse-item" href="{% url 'app:utilities:colors' %}">Colors</a> <a class="collapse-item" href="{% url 'app:utilities:borders' %}">Borders</a> <a class="collapse-item" href="{% url 'app:utilities:animations' %}">Animations</a> <a class="collapse-item" href="{% url 'app:utilities:others' %}">Other</a>
Install the Django Extensions for additional commands:
pip install django-extensions
Add Django Extensions to the INSTALLED_APPS
INSTALLED_APPS = [ ... 'django_extensions' ]
Show URLs and Namespaces (only for out apps, admin urls are removed)
python3 manage.py show_urls

Preparing required components and pages
In summary, these are the steps to create the desired folder structure:
mkdir -p dashboard/apps/components/buttons/templates/buttons mkdir -p dashboard/apps/components/cards/templates/cards mkdir -p dashboard/apps/pages/blank/templates/blank mkdir -p dashboard/apps/pages/charts/templates/charts mkdir -p dashboard/apps/pages/login/templates/login mkdir -p dashboard/apps/pages/pagenotfound/templates/pagenotfound mkdir -p dashboard/apps/pages/password/templates/password mkdir -p dashboard/apps/pages/register/templates/register mkdir -p dashboard/apps/pages/tables/templates/tables mkdir -p dashboard/apps/utilities/animations/templates/animations mkdir -p dashboard/apps/utilities/borders/templates/borders mkdir -p dashboard/apps/utilities/colors/templates/colors mkdir -p dashboard/apps/utilities/others/templates/others
python3 manage.py startapp buttons dashboard/apps/components/buttons python3 manage.py startapp cards dashboard/apps/components/cards python3 manage.py startapp blank dashboard/apps/pages/blank python3 manage.py startapp charts dashboard/apps/pages/charts python3 manage.py startapp login dashboard/apps/pages/login python3 manage.py startapp pagenotfound dashboard/apps/pages/pagenotfound python3 manage.py startapp password dashboard/apps/pages/password python3 manage.py startapp register dashboard/apps/pages/register python3 manage.py startapp tables dashboard/apps/pages/tables python3 manage.py startapp animations dashboard/apps/utilities/animations python3 manage.py startapp borders dashboard/apps/utilities/borders python3 manage.py startapp colors dashboard/apps/utilities/colors python3 manage.py startapp others dashboard/apps/utilities/others
echo "{% extends 'site/base.html' %} {% block content %} {% endblock content %}"> base.html
cp base.html dashboard/apps/components/buttons/templates/buttons cp base.html dashboard/apps/components/cards/templates/cards cp base.html dashboard/apps/pages/blank/templates/blank cp base.html dashboard/apps/pages/charts/templates/charts cp base.html dashboard/apps/pages/login/templates/login cp base.html dashboard/apps/pages/pagenotfound/templates/pagenotfound cp base.html dashboard/apps/pages/password/templates/password cp base.html dashboard/apps/pages/register/templates/register cp base.html dashboard/apps/pages/tables/templates/tables cp base.html dashboard/apps/utilities/animations/templates/animations cp base.html dashboard/apps/utilities/borders/templates/borders cp base.html dashboard/apps/utilities/colors/templates/colors cp base.html dashboard/apps/utilities/others/templates/others
rm base.html

Each of the folders has the same structure, for example the buttons component. We will delete some unnecessary files

Replacing Projects with dynamic data
Replacing the static parts with dynamic content could be achieved by the following approach:
- Identify the dynamic parts
- Move these parts from the site template into the view template
base.html
of the component - Modify frontend
view.py
to generate dynamic content from data
The steps are the same for all components (all items of the side menu).
Find the
Identify dynamic parts in template




![]() | ![]() |
![]() | ![]() |
For every side menu item, their is a corresponding html file in the install folder of the sb-admin-2
template:
Remember the environment variable we create in part 1 for the start of our project
DASHBOARD_ROOT=$(pwd)
cd $DASHBOARD_ROOT find dashboard/apps install/sb-admin-2 -name *.html
Each template file base.html
has a corresponding html file unter sb-admin-2
. Look at the following table to find the mapping:
apps/components/buttons/templates/buttons/base.html | sb-admin-2/buttons.html |
apps/components/cards/templates/cards/base.html | sb-admin-2/cards.html |
apps/pages/blank/templates/blank/base.html | sb-admin-2/blank.html |
apps/pages/charts/templates/charts/base.html | sb-admin-2/charts.html |
apps/pages/login/templates/login/base.html | sb-admin-2/login.html |
apps/pages/pagenotfound/templates/pagenotfound/base.html | sb-admin-2/404.html |
apps/pages/password/templates/password/base.html | sb-admin-2/forgot-password.html |
apps/pages/register/templates/register/base.html | sb-admin-2/register.html |
apps/pages/register/templates/tables/base.html | sb-admin-2/tables.html |
apps/utilities/animations/templates/animations/base.html | sb-admin-2/utilities-animation.html |
apps/utilities/borders/templates/borders/base.html | sb-admin-2/utilities-border.html |
apps/utilities/colors/templates/colors/base.html | sb-admin-2/utilities-color.html |
apps/utilities/others/templates/others/base.html | sb-admin-2/utilities-html |
Each template base.html should have the following content:
{% extends 'site/base.html' %} {% block content %} {% endblock content %}
And each corresponding view.py
file should have the following content, only the template_name
should be different (the name of the template base.html
file)
from django.views import generic class IndexView(generic.TemplateView): template_name = 'buttons/base.html'
So, for each template file, we have to
- locate the corresponding html file from the install folder (see table above)
- copy the content between these tags to the template file:
<!-- Begin Page Content --> <div class="container-fluid"> .... </div> <!-- /.container-fluid -->