Django templates are HTML files with special syntax for inserting dynamic content. The template system separates design from Python code.
Setting Up Templates
Create a templates directory in your app:
polls/
├── templates/
│ └── polls/ # Namespaced to avoid conflicts
│ └── index.html
├── models.py
└── views.py
Or configure a project-wide templates directory in settings.py:
python# mysite/settings.py TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [BASE_DIR / 'templates'], # Project-wide templates 'APP_DIRS': True, # Also check app templates/ directories ... }, ]
Basic Template Syntax
Variables
Display values using double curly braces:
html<!-- polls/templates/polls/detail.html --> <h1>{{ question.question_text }}</h1> <p>Published: {{ question.pub_date }}</p>
Access attributes, methods, dictionary keys, and list items:
html{{ user.username }} <!-- Attribute --> {{ user.get_full_name }} <!-- Method (no parentheses) --> {{ my_dict.key }} <!-- Dictionary key --> {{ my_list.0 }} <!-- List index -->
Filters
Transform values with filters using the pipe | symbol:
html{{ name|lower }} <!-- lowercase --> {{ name|upper }} <!-- UPPERCASE --> {{ name|title }} <!-- Title Case --> {{ text|truncatewords:30 }} <!-- Truncate to 30 words --> {{ date|date:"F j, Y" }} <!-- Format date --> {{ value|default:"N/A" }} <!-- Default if empty --> {{ list|length }} <!-- Count items --> {{ price|floatformat:2 }} <!-- Format number --> {{ html|safe }} <!-- Mark as safe HTML -->
Tags
Tags provide logic and control flow:
html{% if user.is_authenticated %} <p>Welcome, {{ user.username }}!</p> {% else %} <p>Please log in.</p> {% endif %} {% for question in questions %} <li>{{ question.question_text }}</li> {% empty %} <li>No questions available.</li> {% endfor %}
Rendering Templates
Using render()
The most common way to render templates:
python# polls/views.py from django.shortcuts import render from .models import Question def index(request): questions = Question.objects.order_by('-pub_date')[:5] context = { 'questions': questions, 'page_title': 'Latest Questions', } return render(request, 'polls/index.html', context)
html<!-- polls/templates/polls/index.html --> <!DOCTYPE html> <html> <head> <title>{{ page_title }}</title> </head> <body> <h1>{{ page_title }}</h1> <ul> {% for question in questions %} <li>{{ question.question_text }}</li> {% endfor %} </ul> </body> </html>
Template Context
The context is a dictionary mapping variable names to values:
pythoncontext = { 'question': Question.objects.get(pk=1), 'choices': Choice.objects.filter(question_id=1), 'total_votes': 42, 'show_results': True, } return render(request, 'polls/detail.html', context)
Common Pitfalls
- Calling methods with parentheses in templates: In Django templates, write
{{ user.get_full_name }}without parentheses. Adding()will cause a template syntax error. - Forgetting
{% load static %}before using{% static %}: The static tag requires loading the static template tag library first. - Not namespacing app templates: Place templates in
app/templates/app/(not justapp/templates/) to avoid name collisions between apps.
Best Practices
- Keep templates simple: Move complex logic to views or template tags, not inline template code.
- Use template filters for formatting: Apply
|date,|truncatewords, and|defaultfilters instead of formatting data in Python views. - Always escape user input: Django auto-escapes variables by default. Only use
|safewhen you are certain the content is safe HTML.
Summary
- Django templates combine HTML with
{{ variables }}and{% tags %}for dynamic content - Use
render(request, template, context)to pass data from views to templates - Filters (
|lower,|date,|default) transform variable output - Tags (
{% if %},{% for %},{% url %}) provide logic and control flow - Templates are stored in
app/templates/app/directories for proper namespacing
Code Examples
python
from django.shortcuts import render
from .models import Question
def index(request):
questions = Question.objects.order_by('-pub_date')[:5]
context = {
'questions': questions,
'page_title': 'Latest Questions',
}
return render(request, 'polls/index.html', context)