Django Templates

Posted by Daksh on Thursday, August 4, 2022

Where to create a template in an app?

Step 1: Go inside that app
Step 2: Create a sub folder “templates”, at the same level with “migrations” folder
Step 3: Inside this “templates” sub folder, create another sub folder with the same app name “app_name”
Step 4: Create HTML files inside this “app_name” folder

What next?

  1. In app’s view file, import render_to_string from django.template.loader
  2. In the view function, set response_data = render_to_string(“app_name/name_of_template.html”)
  3. return HttpResponse(response_data) in the view function

Method 1: Updating TEMPLATES List of settings.py

  1. Go to settings.py file of the main project, go to TEMPLATES List, add the path in ‘DIRS’, the path should be absolute, therefore use the BASE_DIR helper function defined in the same file
'DIRS': [
            BASE_DIR / "app_name" / "templates"
        ],
  1. The path for the template is BASE_DIR / “app_name” / “templates” / “app_name” / “name_of_template.html”
  2. The DIRS makes Django to look into the templates folder inside app directory, and the remaining path is specified inside render_to_string function of app_name/views.py file

Method 2: Using “APP_DIRS”: True in TEMPLATES List of settings.py

  1. Assuming that all 4 steps mentioned at the top are followed and name convention is done properly
  2. Go to settings.py of main project, and register your app inside INSTALLED_APPS
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # user defined apps
    'app_name',
]

Note: ‘DIRS’ is generally modified in case of Global templates. But for app specific templates, use the naming convention, and register the templates.

Why a nested folder with same app_name is required inside the template folder?

When using the render_to_string(“app_name/name_of_template.html”) function, if an app name directory is not include then it will create a problem if two apps have same template names, as Django merges (under the hood) all the template folders together from all the apps, and it’s the best practise to have a nested app name directory inside each template folder to avoid clashing template file names across different apps.

Role of render function imported from django.shortcuts

This code snippet (given below) is so common that Django has an inbuilt shortcut for it

response_data = render_to_string("app_name/name_of_template.html")
        return HttpResponse(response_data)

This can be reduced to:

return render(request, "app_name/name_of_template.html")

The Django Template Language(DTL)

These are Enhanced HTML files (dynamic html files) to create dynamic pages.
The content is passed as 2nd argument inside render_to_string function and 3rd argument in render function.
The content is passed as a dictionary of key-value pairs, and these are exposed as variables inside the template.html file
To use these variables inside the template, use this syntax: {{variable_name}}, this is not a regular HTML syntax, but it will be picked up by Django while parsing. The dynamic content is injected in html on the server itself, and the client receives a regular static html file. This injection is also known as interpolation, and it can be used anywhere in the html document. For example:

return render(request, "app_name/name_of_template.html", {
    "variable_name_1" : value1,
    "variable_name_2" : value2,
})

# and inside the html file, for this dynamic content, use the syntax {{variable_name}} as a place holder.

Filters

DTL also supports some filters, more information about these can be found in the documentation.{{variable_name|filter_name}}. Filters are required for formatting and outputting data, it is advised to have only business logic inside views.py, and use filters for formatting inside templates. To add a dynamic segment to a path use the add filter:

<img src="{% static "static_segment/"|add:dynamic_segment %}" alt="image">
<!-- load-post is path name defined in urls.py -->
<a href="{% url "load-post" slug=post_slug %}"> </a>

Django VS Code Extension

To recognize DTL in html files, install & use a Django Extension (by Baptiste Darthenay). Inside .vscode/settings.json/ add usage instructions.

Tags & Lists

Used for executing programming logic like for loops and conditional statements inside a Django Template. Refer documentation for more information.
Dynamic URL inside templates:

<body>
<ul>
    {% for iterator in iterable %}
        <li><a href="{% url 'path-name' dynamic_segment_placeholder=iterator %}">{{ iterator | title }}</a></li>
    {% endfor %}
</ul>
</body>
# The path name is defined inside app_name/urls.py
urlpatterns = [
    path("<str:dynamic_segment_placeholder>", views.any_function, name="path-name"),
   ]

Template Inheritance

Create a templates folder in the project directory. If we create this folder inside any app directory, then its scope will be limited to that app only. The template folder inside project directory will have global scope. Make files with sensible names inside this directory such as base.html, layout.html etc.
Use the {% extends "base.html" %} tag inside the child template file (at the very top) to inherit the base template.
Use the {% block content %}Default Content{% endblock %} tag to define a block inside the template file, and this block will be replaced by the child template. Default Content will be used as a fallback if the child template does not override the block. Use the same tag inside child file to inject dynamic data.

Inside settings.py file, add the path of the template folder inside the TEMPLATES list, so that you don’t have to mention entire path in extends tag, and you can just use file name instead.

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            BASE_DIR / "templates"
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
] 

Inside app_name/templates/app_name you can create a new nested directory called includes and create a file called navbar.html. This file can be included inside any template file using the {% include "app_name/includes/navbar.html" %} tag. This is useful for reusing the same code in multiple places. You can also name this directory as partials, and use the same include tag.

The variables available in the parent file are also available in the included file. You can access them by {{ variable_name }}.
To access a python dictionary inside the included file, use {{ dict_name.key }} instead of using {{ dict_name['key'] }}.

You can also use the with tag to pass variables to the included file. For example:

{% include "app_name/includes/navbar.html" with variable_name=value %}

The different parent file will pass different values for the same variable to the included file.

Note: include tag is different from include function used in urls.py file. You can either use absolute path or relative path inside the include tag.

{% include "app_name/includes/navbar.html" %} 
or
{% include "./includes/navbar.html" %} 

Returning 404 Error Page

Don’t use render function to return 404 error page as the render function always renders a “Success” response, and we also want to send the 404 status code along with the html page(response). For such a specific use case, Django also has a shortcut.

from django.http import Http404
# Raise this class as an error
raise Http404("Error Message")

Now this will search for a file named as 404.html inside the template folder(root/template/404.html), and if it doesn’t find it, it will raise a 404 error. If you want to return a custom 404 page, then create a file named as 404.html inside the template folder, and return the custom page from there.

Static Files

For app specific static files, create a folder named as static inside the app directory, and inside this folder, create a folder named as app_name. Inside this folder, create a folder named as css, js, images etc. and put the static files inside these folders.
For project specific static files, create a folder named as static inside the project directory, and inside this folder, create a folder named as css, js, images etc. and put the static files inside these folders.
How to use these static files inside html files?\

  1. Go to settings.py file, and add the path of the static folder inside the TEMPLATES list, so that you don’t have to mention entire path in static tag, and you can just use file name instead. Inside INSTALLED_APPS, the ‘django.contrib.staticfiles’ app should be present.
  2. Scroll down to find STATIC_URL, and change the value to /static/. This setting will not look for this folder for static files instead it informs Django that under which URL static files are to be served.
  3. Inside the html file, use the {% load static %} tag to load the static files.
  4. For css files add the <link rel="stylesheet" href="{% static "css/style.css" %}"> tag inside the <head> tag. If the template is inheriting any other base.html, then add the link into the header of the parent file.
  5. For js files add the <script src="{% static "js/script.js" %}"></script> tag inside the <body> tag. If the template is inheriting any other base.html, then add the link into the footer of the parent file.
  6. Add below snippet inside the head of the base template, in order to include the css & js files inside the child template.
{%  block css_files %}{% endblock %}
{%  block js_files %}{% endblock %}
  1. Inside the child template, add the css & js files inside the block tags. Note that static tag is only available inside the {% load static %} tag.
{% load static %}

{% block css_files %}
    <link rel="stylesheet" href="{% static "app_name/css/style.css" %}">
{% endblock %}
{% block js_files %}
    <script src="{% static "app_name/js/script.js" %}"></script>
{% endblock %}
  1. For global static files, create new directory called static inside the project directory, and add css, js & images folders inside it. Inside the css folder, create a file called style.css. Inside the js folder, create a file called script.js. Inside the images folder, create a file called image.png.\
  2. The STATIC_URL in settings.py does not look into the static directory of the project, it only looks for static folders inside the apps. Therefore, add below code snippet in settings.py file, so that Django can look for static files inside the static folder of the project.
STATIC_URL = '/static/'
STATICFILES_DIRS = [
    BASE_DIR / "static"
]

Note: For using templates of any app with views of another app, inside the urls.py file of the app add:

from django.urls import path, include
app_name = 'app_name'
urlpatterns = [
    path('', views.item_list, name='item-list'),
    path('<int:pk>/', views.item_detail, name='path-name'),
]

# inside the urls.py file of the project
urlpatterns = [
    path('app_name/', include('app_name.urls'), name='app_name')
]

Now inside the template of any other app, you can add the link as {% url 'app_name:path-name' item.id %}.