Recommended preparation: set up a Django project and read the Django tutorial overview before trying the code.
A Django template uses {% for item in items %} to repeat markup for a list or QuerySet. The examples begin with a plain Python list, then show database records from the same Student model used throughout this section.
views.py — pass employee records to the template
from django.shortcuts import render
def employees(request):
people = [
{"id": 123, "name": "Ravi", "job": "Manager"},
{"id": 124, "name": "Raju", "job": "Driver"},
{"id": 125, "name": "Kiran", "job": "Store Keeper"},
]
return render(request, "employees.html", {"people": people})
For display-only records, a list of dictionaries is sufficient. When records must be saved in a database, use a real Django model with models.CharField and other field definitions instead of assigning Python types such as str to class attributes.
templates/employees.html
<ul>
{% for person in people %}
<li>{{ person.id }} — {{ person.name }} ({{ person.job }})</li>
{% empty %}
<li>No employees found.</li>
{% endfor %}
</ul>
Expected output
123 — Ravi (Manager)
124 — Raju (Driver)
125 — Kiran (Store Keeper)
my_app/urls.py
from django.urls import path
from . import views
urlpatterns = [
path("employees/", views.employees, name="employees"),
]
students/models.py — database-backed Student model
from django.db import models
class Student(models.Model):
name = models.CharField(max_length=100)
cl = models.CharField(max_length=20, db_column="class")
mark = models.PositiveSmallIntegerField()
gender = models.CharField(max_length=20)
def __str__(self):
return self.name
students/views.py
from django.shortcuts import render
from .models import Student
def student_list(request):
students = Student.objects.order_by("id")
return render(request, "students/list.html", {"students": students})
students/templates/students/list.html
<table>
<thead><tr><th>ID</th><th>Name</th><th>Mark</th></tr></thead>
<tbody>
{% for student in students %}
<tr><td>{{ student.id }}</td><td>{{ student.name }}</td><td>{{ student.mark }}</td></tr>
{% empty %}
<tr><td colspan="3">No student records yet.</td></tr>
{% endfor %}
</tbody>
</table>
Template: a counter and an if condition
{% for student in students %}
<p>{{ forloop.counter }}. {{ student.name }}
{% if student.mark >= 50 %}(Pass){% else %}(Review){% endif %}
</p>
{% endfor %}
forloop.counter starts at 1; forloop.counter0 starts at 0. Django template syntax is not Python syntax: use template tags rather than Python list comprehensions inside HTML.
Nested template loops
{% for week in weekly_tasks %}
<h3>Week {{ forloop.counter }}</h3>
<ul>
{% for task in week %}
<li>{{ task }}</li>
{% endfor %}
</ul>
{% endfor %}
Do filtering and ordering in the view or model layer, not with complex template conditions. For database lists, use QuerySet filters and slicing and consider pagination for large tables. Templates should focus on presentation.
View: only the first 20 passing students, alphabetically
students = Student.objects.filter(mark__gte=50).order_by("name")[:20]
{% for %} needs a matching {% endfor %}.select_related() or prefetch_related() when accessing related objects.For Python’s own iteration statements, see the Python for-loop tutorial. Django templates use a restricted presentation language rather than executing general Python expressions.
Use the {% empty %} block inside {% for %} to display fallback markup.
Yes. QuerySets are iterable and commonly passed to templates through context dictionaries.
No. A list of dictionaries or simple objects is suitable until you need persistent database records.
Previous: Query string parameters
Next: CSS, JavaScript, jQuery and static files
Browse the complete Django tutorial series.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.