ایجاد تمپلیت
همانطور که پیشتر اشاره شد، الگوهای آدرس (URL patterns) در جنگو، نقش یک پل را بین درخواستهای کاربر و منطق برنامه ایفا میکنند. هر الگوی URL، یک آدرس وب را به یک تابع view متصل میکند. این تابع view، به عنوان مسئول اصلی پردازش درخواست، تصمیم میگیرد که چه دادههایی باید جمعآوری شوند، چگونه پردازش شوند و چه اطلاعاتی به کاربر ارائه گردد.
پس از آن، دادهها به یک تمپلیت (template) منتقل میشوند — که در واقع فایلهای HTML هستند و نحوه نمایش بصری اطلاعات را تعیین میکنند. برای اینکه جنگو بتواند این تمپلیتها را پیدا کند، باید دایرکتوری templates را به صورت دستی در مسیر ریشه پروژه (هم سطح فایل manage.py) و یا داخل دایرکتوری پروژه (در اینجا tutorial) ایجاد کنیم، زیرا این دایرکتوری بهصورت پیشفرض وجود ندارد. ایجاد این دایرکتوری، گام اول و ضروری برای سازماندهی منطق نمایشی برنامه و جداکردن لایههای طراحی از منطق کسبوکار است.
└───Tutorial-Django
│ manage.py
└───tutorial
│ asgi.py
│ settings.py
│ urls.py
│ wsgi.py
│ __init__.py
│
└───templates
پس از ایجاد دایرکتوری templates در مسیر ریشه پروژه، باید جنگو را مطلع کنیم تا این دایرکتوری را به عنوان مکانی برای جستجوی فایلهای تمپلیت شناسایی کند. برای این منظور، به فایل settings.py درون دایرکتوری جنگوی پروژه (هم سطح فایلهای urls.py و wsgi.py) رفته و در بخش TEMPLATES، پارامتر DIRS:[ ] را ویرایش میکنیم. در این قسمت، مسیر دایرکتوری templates را به صورت مطلق (absolute path) اضافه میکنیم تا جنگو بتواند فایلهای HTML ما را در هر زمانی که از آنها استفاده میشود، پیدا و بارگذاری کند. این تنظیم، گام ضروری برای فعالسازی سیستم تمپلیتهای شخصیسازیشده در پروژه است و بدون آن، حتی اگر فایلهای تمپلیت وجود داشته باشند، جنگو قادر به تشخیص و استفاده از آنها نخواهد بود.
tutorial/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR, 'tutorial/templates'], # 🡨 HERE
'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',
],
},
},
]
البته دستور فوق در نسخههای جدید جنگو عرضه شده و در صورت عدم کارایی میتوان از دستور زیر نیز بهره گرفت
import os
TEMPLATES = [
'...',
'DIRS': [os.path.join(BASE_DIR, 'tutorial/templates'),],
'...'
]
حال که جنگو را به درستی آموزش دادهایم تا دایرکتوری templates را به عنوان مکان پیشفرض برای فایلهای تمپلیت شناسایی کند، میتوانیم اولین تمپلیت خود را ایجاد کنیم. برای آدرس Home (یا همان ریشه وبسایت)، یک فایل با نام base.html درون دایرکتوری templates ایجاد میکنیم. این فایل به عنوان قالب پایه (Base Template) عمل میکند و ساختار اصلی صفحه — شامل هدر، فوتر، لینکهای CSS و JavaScript، و سایر المانهای مشترک — را تعریف مینماید. با استفاده از این قالب پایه، میتوانیم در صفحات مختلف از طریق مکانیزم extends، از همان ساختار اصلی استفاده کنیم و تنها بخشهای متغیر را جایگزین نماییم؛ این رویکرد نه تنها کدنویسی را تمیزتر و قابل نگهداریتر میکند، بلکه یکنواختی ظاهری سایت را نیز تضمین مینماید.
tutorial/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> Araz.S.X </title>
</head>
<body>
<h1> Core-App Home Page </h1>
<p> page is rendered from <b>"tutorial/templates/base.html"</b> template </p>
</body>
</html>
حال میبایست، با استفاده از قابلیتهای view نسبت به چگونگی نمایش مسیر آدرس home/ اقدام نماییم:
coreapp/views.py
from django.shortcuts import render
# Create your views here.
def Home(request):
return render(request, 'base.html')
در اینصورت نمایش صفحه در مسیر آدرس http://127.0.0.1:8000 بدین شکل خواهد بود

برای رعایت ساختار استاندارد و افزایش قابلیت نگهداری و تکرارپذیری پروژه، تمپلیتهای عمومی و اساسی — مانند navbar.html, footer.html, base.html یا هر المانی که در بیشتر صفحات پروژه استفاده میشود — در دایرکتوری ریشه پروژه (در مسیر projectname/templates/) قرار میگیرند. این تمپلیتها به عنوان پایه (base templates) برای سایر صفحات به کار میروند و از تکرار کد جلوگیری میکنند. در مقابل، تمپلیتهای اختصاصی هر اپ (مانند لیست محصولات، صفحه ورود کاربر، و غیره) در زیرمجموعهٔ /appname/templates/ داخل همان اپ قرار میگیرند. این ساختار دوگانه، مسئولیتپذیری را مشخص میکند: تمپلیتهای عمومی در سطح پروژه و تمپلیتهای اپ-محور در سطح اپ. بدین منظور، همانطور که در سطح پروژه یک دایرکتوری templates/ ایجاد میکنیم، در هر اپ نیز یک دایرکتوریtemplates/ با همان الگوی سازمانیافته ایجاد میشود تا ساختار پروژه تمیز، مقیاسپذیر و مطابق با بهترین روشهای جنگو باشد.
└───Tutorial-Django
│ manage.py
│
├───clientapp
│ │
│ ├───migrations
│ └───templates
├───coreapp
│ │
│ ├───migrations
│ └───templates
└───tutorial
│
└───templates
base.html
زبان تمپلیت جنگو
در جنگو، زبان تمپلیت از سه مؤلفه اصلی تشکیل شده است: متغیرها (Variables)، فیلترها (Filters) و تگهای تمپلیت (Template Tags).
⮜ تگها {% tag %} برای کنترل منطق و جریان در تمپلیت استفاده شده و شامل دستورات منطقی مانند شرط، حلقه و بارگذاری ماژولها هستند مانند {% if %}, {% for %}, {% url %} .
⮜ متغیرها {{ variable }} برای نمایش دادههای ارسالشده از view استفاده میشوند.
⮜ فیلترها {{ value|filter }} برای تبدیل یا اصلاح مقدار متغیرها قبل از نمایش به کار میروند مانند {{ date|date:"Y-m-d" }} یا {{name|upper}}
بطور خلاصه، زبان تمپلیت جنگو شامل تگها، متغیرها و فیلترها در کنار هم، با قدرت و انعطافپذیری خود، امکان ساخت تمپلیتهای پویا و هوشمند را فراهم میکنند.
تگها - Tags
تگها در جنگو ابزاری هستند برای افزودن منطق برنامهنویسی به قالبها (Template) و نشان میدهند که در این بخش چیزی فراتر از HTML ساده وجود دارد. در واقع تگها به جنگو میگویند که دستورات خاصی را درون براکتهای {% tag %} اجرا کند. جنگو مجموعهای متنوع از دهها تگ را فراهم کرده که به کمک آنها میتوان منطقهای مختلف را در قالبها پیادهسازی کرد. ساختار کلی همه تگها به صورت {% tag %} است.
-
برخی تگها متن یا محتوای مشخصی را در خروجی تولید میکنند.
-
برخی دیگر جریان نمایش را با استفاده از حلقهها
{% for %}یا شرطها{% if %}کنترل میکنند. -
دستهای هم برای بارگذاری دادههای خارجی در قالب بهکار میروند تا در ادامه بتوان از آنها توسط متغیرها استفاده کرد.
تگ Extends
تگ {% extends %} در جنگو برای ارثبری از یک تمپلیت پایه استفاده میشود. به بیان ساده، وقتی تمپلیتی از extends استفاده کند، یعنی محتوای آن بر اساس یک قالب والد (Parent Template) ساخته خواهد شد.
در این روش ابتدا یک تمپلیت اصلی (مثلاً base.html) تعریف میشود و سپس صفحات دیگر با استفاده از تگ extends محتوای خود را در بخشهای مشخصشده به آن اضافه میکنند. به این ترتیب هر صفحه میتواند محتوای متفاوتی داشته باشد، اما همگی از ساختار اصلی یکسانی پیروی میکنند.
مزیت این کار این است که نیاز به تکرار کدها از بین میرود. به کمک extends میتوان بارها از یک قالب پایه و حتی متغیرهای آن استفاده کرد و فقط بخشهای متغیر را در هر صفحه تغییر داد.
{% extends 'base.html' %}
تگ {% extends %} یک آرگومان ورودی میگیرد که نام تمپلیت والد (Parent Template) است. وقتی یک تمپلیت فرزند که از extends استفاده میکند، رندر میشود، جنگو ابتدا تمپلیت والد را بهعنوان یک اسکلت اصلی بارگذاری میکند و سپس محتوای فرزند را طبق بلوکهای تعریفشده با {% block %} ... {% endblock %} در جای مناسب قرار میدهد.
⚠️ تگ {% extends %} باید اولین تگ در تمپلیت باشد (قبل از هر تگ دیگری، حتی تگهای بارگذاری مثل {% load %}). در غیر این صورت، جنگو آن را نادیده میگیرد و ارثبری انجام نخواهد شد.
تگ Block
تگ {% block %} همراه با {% endblock %} برای مشخص کردن بخشهای قابل تغییر در یک تمپلیت والد استفاده میشود. وقتی یک تمپلیت پایه (Base Template) ساخته میشود، بخشهایی که قرار است توسط تمپلیتهای فرزند تغییر کنند یا مقدار جدید بگیرند داخل بلاکها قرار میگیرند. سپس در تمپلیت فرزند، میتوان همان بلاکها را بازنویسی (override) کرد و محتوای دلخواه را جایگزین آنها نمود. به این ترتیب ساختار کلی صفحه ثابت میماند، اما محتوای بخشهای خاص در هر صفحه میتواند متفاوت باشد.
{% block blockname %}
Block Content
{% endblock blockname %}
━◦○◦ نکتههای تکمیلی ◦○◦━
- ‼ باید توجه داشت که blockname نام بلاک بوده و باید یکتا باشد.
- ‼ میتوان چندین بلاک مختلف در یک تمپلیت والد تعریف کرد.
- ‼ بلاکها قابلیت تو در تو (nested) ندارند.
- ‼ اگر تمپلیت فرزند بلاکی را بازنویسی نکند، همان محتوای پیشفرض داخل بلاک تمپلیت پایه نمایش داده میشود.
- ‼ برای استفاده از محتوای قبلی یک بلاک همراه با محتوای جدید، میتوان از کلمه کلیدی
{{ block.super }}در بلاک فرزند استفاده کرد
tutorial/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> {% block title %} Araz.S.X {% endblock title %} </title>
{% block style %}{% endblock style %}
</head>
<body>
{% block content %}{% endblock content %}
{% block script %}{% endblock script %}
</body>
</html>
در فایل base.html با استفاده از تگ {% block %} مشخص میکنیم که در این بخش فضایی وجود دارد تا قالبهای فرزند، هنگام ارثبری از base.html، محتوای مربوط به همان بلوک (blockname) را در آن قرار دهند. به طور معمول، در این فایل میتوان یک بلوک برای عنوان صفحه (مانند block title) و یک بلوک دیگر برای محتوای اصلی (مانند block content) تعریف کرد. قالبهای فرزند هنگام ارثبری از base.html، محتوای مورد نیاز خود را در این بلوکها قرار میدهند. برای مثال، عنوان صفحه در تگ title به کمک block title بازنویسی میشود و محتوای متغیر هر صفحه در ناحیهی block content درج خواهد شد. با این کار ساختار کلی سایت، شامل بخشهای مشترکی مثل هدر یا توضیحات ثابت، یکسان باقی میماند، اما بخشهای خاص هر صفحه بهطور مستقل قابل سفارشیسازی است. این روش علاوه بر جلوگیری از تکرار کد، باعث نظم و انعطافپذیری بیشتر در مدیریت قالبها میشود.
اکنون یک قالب برای صفحه اصلی ایجاد میکنیم که ساختار کلی خود را از فایل base.html به ارث میبرد و تنها بخش محتوای مخصوص به خودش را جایگزین میکند. با {% extends "base.html" %} اعلام میکنیم که این قالب فرزند است و از ساختار base.html استفاده میکند. در بلوک title، مقدار پیشفرض عنوان (یعنی همان block.super) حفظ شده و عبارت Home به آن اضافه شده و در نهایت در بلوک content، محتوای اختصاصی صفحه اصلی نوشته شده که هنگام رندر جایگزین بخش مربوطه در قالب پایه میشود.
coreapp/templates/home.html
{% extends "base.html" %}
{% block title %} {{ block.super }} | Home {% endblock title %}
{% block content %}
<h1> Tutorial Home Page </h1>
<p> page is rendered from <b>"base.html"</b> as BASE & <b>"home.html"</b> as CONTENT Templates </p>
{% endblock content %}
حال کافیست با جایگزین کردن 'home.html' به جای 'base.html' در تابع Home() در views.py به نمایش زیر در رابط کاربری دست یابیم

تگ Include
تگ {% include %} برای بارگذاری و نمایش یک تمپلیت دیگر در داخل تمپلیت جاری استفاده میشود. به بیان ساده، این تگ مثل یک «کپیپیست هوشمند» عمل میکند و باعث میشود قطعات تکراری کد (مثل footer ،navbar یا sidebar) را فقط یک بار تعریف کنیم و در چندین صفحهی مختلف استفاده کنیم.
{% include 'template.html' %}
برای نمونه ساختار سادهای برای تمپلیتهای navbar و footer در ردیف تمپلیتهای پایه ایجاد میکنیم.
tutoriaL/templates/navbar.html
<nav>
<h1>ARAZ. S. X. LOGO</h1>
<hr>
</nav>
tutoriaL/templates/footer.html
<footer>
<hr>
<small>Copyright © 2025</small>
</footer>
حال میتوان، تمپلیتها را به ساختار تمپلیت پایه base با بهرهگیری از تگ include اضافه نمود
tutorial/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> {% block title %} Araz.S.X {% endblock title %} </title>
{% block style %}{% endblock style %}
</head>
<body>
{% include "navbar.html" %} <!-- NAVBAR TEMPLATE -->
{% block content %}{% endblock content %}
{% include "footer.html" %} <!-- FOOTER TEMPLATE -->
{% block script %}{% endblock script %}
</body>
</html>
صفحه Home به شکل زیر نمایش داده خواهد شد

━◦○◦ نکتههای تکمیلی ◦○◦━
- ‼ میتوان متغیرها را هم به تمپلیت
includeشده ارسال کرد.
{% include "sidebar.html" with section=posts %}
در این حالت متغیر section با محتوای دادههای posts در فایل sidebar.html در دسترس خواهد بود.
- ‼ گر فایل تمپلیت وجود نداشته باشد، میتوان با عبارت
ignore missingاز بروز خطا جلوگیری کرد.
{% include "optional.html" ignore missing %}
- ‼ بهترین استفاده از
includeبرای بخشهای تکراری و کوچک است (مثل هدر، فوتر، فرم ورود و ...). برای ساختارهای بزرگتر، بهتر است ازextendsاستفاده شود. - ‼ تگ
includeمیبایست دقیقاً در جایی قرار داده شود که قرار است محتوا نمایش داده شود.
تگ URL
تگ {% url %} برای تولید یا برگرداندن آدرس URL استفاده میشود. مزیت اصلی این تگ این است که به جای نوشتن مستقیم آدرسها (که ممکن است بعداً تغییر کنند)، از نام مسیر (name) که در هنگام تعریف الگوهای URL در urls.py مشخص شده، استفاده میکنیم. به این ترتیب اگر مسیر تغییر کند، فقط کافیست در urls.py آن را اصلاح کنیم و نیازی به تغییر در همهی تمپلیتها نخواهیم داشت.
{% url 'urlname' %}
همچنین در صورتیکه URL، در urls.py نیاز به پارامتر داشته باشد، میتوان پارامترها را بعد از نام URL مطرح کرد که به دو صورت فقط مقادیر و یا کلید-مقدار (key=value) نیز قابل انتقال خواهد بود
⮜ فقط مقادیر (Position-Based)
پارامترها را به همان ترتیبی که در urls.py تعریف شدهاند، پشت سر هم قرار میگیرند.
{% url 'urlname' value1 value2 %}
⮜ کلید—مقدار (Keyword Arguments)
پارامترها با نامشان ارسال میگردند. این روش خواناتر است و ترتیب اهمیت ندارد.
{% url 'urlname' param=value1 argument=value2 %}تگ For
تگ {% for %} برای اجرای یک حلقه روی مجموعهای از دادهها (مثل لیست، دیکشنری، QuerySet و ...) استفاده میشود. در هر تکرار حلقه، ویژگیهای آیتم جاری در یک متغیر محلی در دسترس قرار میگیرد.
{% for item in list %}
{{ item }}
{% endfor %}
در جنگو میتوان با استفاده از کلمهی کلیدی reversed حلقهی for را بهصورت معکوس اجرا کرد.
{% for item in list reversed %}
{{ item }}
{% endfor %}
با استفاده از {% empty %} میتوان مشخص کرد اگر لیست خالی بود چه چیزی نمایش داده شود
<ul>
{% for name in names %}
<li>{{ name }}</li>
{% empty %}
<li>No Body Found</li>
{% endfor %}
</ul>
━◦○◦ نکتههای تکمیلی ◦○◦━
داخل حلقهی for، جنگو یک متغیر ویژه به نام forloop در اختیار میگذارد که اطلاعات مفیدی دارد:
⮜ forloop.counter 🡠 شماره آیتم (شروع از 1)
⮜ forloop.counter0 🡠 شماره آیتم (شروع از 0)
⮜ forloop.first 🡠 درست (True) اگر اولین آیتم باشد
⮜ forloop.last 🡠 درست (True) اگر آخرین آیتم باشد
⮜ forloop.revcounter 🡠 شمارش معکوس (شروع از تعداد آیتمها)
{% for name in names %}
{% if forloop.first %}
<strong>First Person: {{ name }}</strong><br>
{% elif forloop.last %}
<em>Last Person: {{ name }}</em><br>
{% else %}
{{ forloop.counter }} - {{ name }}<br>
{% endif %}
{% endfor %}تگ If
تگ {% if %} برای بررسی شرایط منطقی روی یک متغیر یا عبارت استفاده میشود. اگر نتیجهی شرط True باشد (یعنی وجود داشته باشد، خالی نباشد و یا مقدار گزاره False نباشد) محتوای داخل بلوک نمایش داده میشود، در غیر این صورت، نادیده گرفته خواهد شد.:
{% if condition %}
<p> statement! </p>
{% elif not condition %}
<p> statement! </p>
{% elif condition and ( condition or condition ) %}
<p> statement! </p>
{% else %}
<p> statement! </p>
{% endif %}
condition هر متغیر یا عبارتی است که قابلیت ارزیابی به True / False داشته باشد.
━◦○◦ نکتههای تکمیلی ◦○◦━
شرایطی که False محسوب میشوند
⮜ مقدار False
⮜ مقدار None
⮜ رشتهی خالی ''
⮜ لیست یا دیکشنری خالی [] یا {}
⮜ عدد صفر 0
در غیر این صورت شرط، True در نظر گرفته میشود.
متغیرها – Variables
همانطور که گفته شد، متغیرها در جنگو، داخل {{ }} قرار میگیرند و برای انتقال دادهها به قالبها (templates) و نمایش محتوای پویا به کار میروند. برای استفاده از متغیرها باید ابتدا مقادیر موردنظر را در تابع view تعریف کرده و سپس آنها را به قالب ارسال کنیم. بهعنوان مثال، فرض کنیم در مسیر articles/ میخواهیم لیستی از مقالات به همراه سطح تخصص هر مقاله (عددی بین ۱ تا ۳) را به قالب بفرستیم. در نهایت، این دادهها در قالب طوری نمایش داده میشوند که سطح تخصص هر مقاله از آسان تا سخت مشخص شود.
ابتدا در فایل views.py به تابع مختص URL رفته و متغیر مربوطه را ایجاد میکنیم:
coreapp/views.py
def Articles(request):
title = 'Artificial Intelligence'
difficulty = 3
context = {'article':title, 'level': difficulty}
return render(request, 'articles.html', context)
سپس به فایل تمپلیت articles.html رفته و متغیرها را در ساختار تمپلیت استفاده میکنیم:
coreapp/templates/articles.html
{% extends "base.html" %}
{% block title %} {{block.super}} | Articles {% endblock title %}
{% block content %}
<h1> Article Title: {{ article }} </h1>
<p> Article Level:
{% if level == 1 %}
<b>Easy</b>
{% elif level == 2 %}
<b>Normal</b>
{% else %}
<b>Hard</b>
{% endif %}
</p>
{% endblock %}
همانگونه که در کد فوق قابل مشاهده میباشد، ابتدا دادههای عنوان (title) و درجه سختی (difficulty) در تابع Articles() در views برای مقاله تعریف شده و سپس بهترتیب توسط متغیرهای article و level به تمپلیت برای ارائه در رابط کاربری انتقال داده شدند. برای دسترسی به مقادیر این متغیرها در تمپلیت، کافیست از {{ title }} و همچنین {{ level }} استفاده کنیم
⚠️ در صورتیکه از متغیر در داخل تگ تمپلیت {% %} استفاده میکنیم، دیگر نیازی به استفاده از {{ }} نخواهد بود.
درنهایت با تعریف الگوی URL برای درخواست، میتوان فرآیند را تکمیل نمود:
coreapp/urls.py
urlpatterns = [
path('articles/', views.Articles, name='Articles'),
]
حال در مرورگر، اگر آدرس 127.0.0.1:8000/articles/ وارد کنیم، تصویر ذیل را مشاهده خواهیم کرد.

حال اگر فرض بگیریم لیستی از مقالهها داشته باشیم که بهصورت دیکشنری تعریف شده باشند — دیکشنریها برای ذخیره مقادیر داده بهصورت آرایههای key:value استفاده میشوند — و بخواهیم مقالات را در مسیر آدرس articles/ لیست کنیم، تعریف تابع مربوطه در views.py به این صورت خواهد شد
coreapp/views.py
def Articles(request):
ArticlesDictionary = [
{'id': '1', 'title': 'Artifical Intelligence', 'level': 3},
{'id': '2', 'title': 'Sport', 'level': 1},
{'id': '3', 'title': 'Nature', 'level': 2},
]
context = {'articles':ArticlesDictionary}
return render(request, 'articles.html', context)
و تمپلیت مربوطه بدین صورت تغییر شکل خواهد داد:
coreapp/templates/articles.html
{% extends "base.html" %}
{% block title %} {{block.super}} | Articles {% endblock title %}
{% block content %}
<h1> Articles List: </h1>
<ul>
{% for article in articles %}
<li><b>{{ article.title }}</b> - <small>Difficulty Level: {{ article.level }} </small></li>
{% endfor %}
</ul>
{% endblock %}
اگر دوباره وارد آدرس زیر articles/ شویم، در مرورگر تصویر ذیل را مشاهده خواهیم کرد

فیلترها – Filters
حال که با دو مدل از زبانهای تمپلیت در جنگو — تگها و متغیرها — و نحوه استفاده از آنها آشنا شدیم، در این بخش به شرح یکی دیگر از قابلیتهای مهم تمپلیتها، یعنی فیلترها (Filters)، خواهیم پرداخت.
فیلترها در تمپلیت جنگو ابزاری هستند برای تغییر، پردازش یا قالببندی مقدار متغیرها پیش از آنکه در خروجی HTML نمایش داده شوند. به بیان سادهتر، اگر بخواهیم دادهای که از View ارسال شده به شکل متفاوت یا خواناتر در رابط کاربری نمایش داده شود، میتوانیم از فیلترها استفاده کنیم. فیلترها با علامت | (خط عمودی یا pipe) بعد از متغیر نوشته میشوند.
فرض کنید در تابع view متغیر customtext با مقدار "django is awesome" را به تمپلیت ارسال کردهایم. اگر بخواهیم این متن به صورت حروف بزرگ نمایش داده شود، میتوانیم از فیلتر upper استفاده کنیم:
{{ customtext|upper }}
خروجی:
DJANGO IS AWESOMEفیلترهای مربوط به متن (String Filters)
فیلترهای متنی یا String Filters در جنگو برای دستکاری، فرمتدهی و تغییر محتوای متنی به کار میروند. این فیلترها به ما اجازه میدهند دادههای متنی که به تمپلیت ارسال شدهاند را بدون تغییر در سطح پایگاه داده یا لاجیک برنامه، به شکل دلخواه در خروجی نمایش دهیم.
برای نمونه میتوانیم تمام حروف را کوچک یا بزرگ کنیم (lower, upper)، اولین حرف جمله یا هر کلمه را بزرگ کنیم (capfirst, title)، بخشهایی از متن را حذف کنیم (cut)، یا متن طولانی را کوتاه نماییم (truncatewords, truncatechars) و با length یا length_is میتوانیم طول متنها یا رشتهها را بسنجیم. همچنین فیلترهایی مانند linebreaks و linenumbers به ما کمک میکنند متنهای چندخطی را به صورت قالببندیشده نمایش دهیم. به طور خلاصه،
این دسته از فیلترها ابزار قدرتمندی برای نمایش حرفهای و کاربرپسند دادههای متنی در تمپلیت هستند.
{{ "hello world!"|upper }} ┈┈┈⮞ "HELLO WORLD!" <!-- Returns the text in upper case letters. -->
{{ "HELLO WORLD!"|lower }} ┈┈┈⮞ "hello world!" <!-- Returns the text in lower case letters. -->
{{ "hello world!"|title }} ┈┈┈⮞ "Hello World!" <!-- Upper cases the first character of each word in a text, all other characters are converted to lower case. -->
{{ "hello world!"|capfirst }} ┈┈┈⮞ "Hello world!" <!-- Returns the first letter in uppercase. -->
{{ "Hello World!"|slugify }} ┈┈┈⮞ "hello-world" <!-- Converts text into one long alphanumeric-lower-case word. -->
{{ "Hello World!"|make_list }} ┈┈┈⮞ ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'] <!-- Converts a value into a list object. -->
{{ "Hello World!"|cut:"l" }} ┈┈┈⮞ "Heo Word!" <!-- Removes any specified character or phrases -->
{{ "Hello World!"|slice:":4" }} ┈┈┈⮞ "Hell" <!-- Returns a specified slice of a text or object. -->
{{ "Hello World!"|truncatechars:4 }} ┈┈┈⮞ "Hel..." <!-- Shortens a string into the specified number of characters. -->
{{ "Hello World!"|truncatewords:1 }} ┈┈┈⮞ "Hello..." <!-- Shortens a string into the specified number of words. -->
{{ "Hello World!"|wordcount }} ┈┈┈⮞ 2 <!-- Returns the number of words in a text. -->
{{ "Hello World!"|length }} ┈┈┈⮞ 12 <!-- Returns the number of items in an object, or the number of characters in a string. -->
{{ "Hello World!"|length_is:12 }} ┈┈┈⮞ True <!-- Returns True if the length is the same as the specified number -->
{{ "Hello World!"|center:20 }} ┈┈┈⮞ " Hello World! " <!-- Centers the value in the middle of a specified width. -->
{{ "Hello World!"|ljust:20 }} ┈┈┈⮞ "Hello World! " <!-- Left aligns the value according to a specified width -->
{{ "Hello World!"|rjust:20 }} ┈┈┈⮞ " Hello World!" <!-- Right aligns the value according to a specified width -->
{{ "Hello \n World!"|linebreaks }} ┈┈┈⮞ <p>Hello <br/> World!</p> <!-- Returns the text with <br> instead of line breaks, and <p> instead of more than one line break. -->
{{ "Hello \n World!"|linebreaksbr }} ┈┈┈⮞ Hello <br/> World! <!-- Returns the text with <br> instead of line breaks. -->
{{ "<b>Hello World!</b>"|striptags }} ┈┈┈⮞ Hello World! <!-- Removes HTML tags from a text. -->
{{ "I'm Araz"|addslashes }} ┈┈┈⮞ "I\'m Araz" <!-- Adds a slash before any quote characters -->فیلترهای مربوط به اعداد (Number Filters)
فیلترهای رقم و اعداد یا Number Filters در جنگو برای پردازش و فرمتدهی دادههای عددی به کار میروند. این فیلترها به ما امکان میدهند تا اعداد را به شکلی خواناتر و مناسبتر برای نمایش در خروجی قالب تبدیل کنیم.
برای مثال میتوانیم یک عدد را با مقدار دیگری جمع کنیم (add)، تعداد اعشار را کنترل نماییم (floatformat)، یا بررسی کنیم که آیا عدد بر عدد دیگری بخشپذیر است (divisibleby). همچنین فیلترهایی مثل filesizeformat به ما کمک میکنند اندازه فایلها را به واحدهای قابل فهم برای کاربر (KB, MB, GB) نشان دهیم.
این فیلترها باعث میشوند دادههای عددی به صورت کاربرپسند، دقیق و متناسب با نیاز پروژه در تمپلیتها نمایش داده شوند.
{{ 5|add:10 }} ┈┈┈⮞ 15 <!-- Adds a specified value. -->
{{ 15|add:-10 }} ┈┈┈⮞ 5 <!-- Subtraction a specified value. -->
{{ 10|divisibleby:5 }} ┈┈┈⮞ True <!-- Returns True if the value can be divided by the specified number, otherwise it returns False. -->
{{ 123456789|filesizeformat }} ┈┈┈⮞ 117.7 MB <!-- Returns a number into a file size format. -->
{{ 3.14159|floatformat:2 }} ┈┈┈⮞ 3.14 <!-- Rounds floating numbers to a specified number of decimals, default one decimal. -->فیلترهای مربوط به تاریخ (Date/Time Filters)
فیلترهای Date/Time در جنگو برای فرمتدهی و پردازش دادههای زمانی به کار میروند و نقش مهمی در نمایش درست و کاربرپسند تاریخها و زمانها دارند. با استفاده از این فیلترها میتوانیم تاریخها (date) و یا زمان (time) را در قالب دلخواه نمایش و با فرمت مشخص نشان دهیم، یا فاصله زمانی بین دو تاریخ را به شکل قابل فهم محاسبه کنیم (timesince, timeuntil).
این فیلترها مخصوصا زمانی مفید هستند که بخواهیم دادههای خام دیتابیس (که معمولا به فرمت استاندارد ذخیره میشوند) را به فرمتی قابل درک برای کاربر تبدیل کنیم، مثل نمایش تاریخ پستها، مدتزمان باقیمانده تا یک رویداد یا فاصله زمانی از آخرین بهروزرسانی.
به این ترتیب، فیلترهای تاریخ و زمان ابزاری ضروری برای هر پروژهای هستند که دادههای زمانی در آن نقش دارند.
{{ mybirthdate|date:"Y-m-d" }} ┈┈┈⮞ 1984-04-04 <!-- Returns dates in the specified format. -->
{{ mybirthdate|time:"H:i" }} ┈┈┈⮞ 14:35 <!-- Returns a time in the specified format. -->
{{ mybirthdate|timesince }} ┈┈┈⮞ 20 years, 10 months <!-- Returns the difference between two datetimes. -->
{{ mybirthdate|timesince:nowrouz }} ┈┈┈⮞ 11 months, 13 days
{{ marslanding|timeuntil }} ┈┈┈⮞ 24 years, 7 months <!-- Returns the difference between two datetimes. -->
{{ marslanding|timeuntil:moonlanding }} ┈┈┈⮞ 80 years, 10 months⌘ فیلترهای مربوط به لیستها و دادهها (List & Data Filters)
List & Data Filters در جنگو برای کار با مجموعهها (لیست، کوئریست، دیکشنری و …) استفاده میشوند و امکان دسترسی، مرتبسازی یا تغییر نحوه نمایش دادهها را فراهم میکنند. برای مثال میتوانیم اولین یا آخرین عنصر یک لیست را نمایش دهیم (first, last)، یا اعضای لیست را با یک جداکننده خاص به هم متصل کنیم (join). اگر دادهها به صورت دیکشنری باشند، میتوانیم آنها را بر اساس یک کلید مرتب کنیم (dictsort, dictsortreversed). همچنین میتوانیم یک بخش خاص از لیست را انتخاب کنیم (slice)، یک عضو تصادفی از لیست نمایش دهیم (random)، یا طول دادهها را بررسی کنیم (length). این دسته از فیلترها زمانی بسیار کاربردی هستند که دادههای پیچیده از سمت ویو به تمپلیت ارسال میشوند و نیاز داریم آنها را به شکل سادهتر، مرتبتر و قابلفهمتر در خروجی نشان دهیم.
{{ mylist|first }} <!-- Returns the first item of an object (for Strings, the first character is returned). -->
{{ mylist|last }} <!-- Returns the last item of an object (for Strings, the last character is returned). -->
{{ mylist|join:", " }} <!-- Returns the items of a list into a string. -->
{{ mylist|random }} <!-- Returns a random item of an object -->
{{ mylist|slice:":2" }} <!-- Returns a specified slice of a text or object. -->
{{ mylist|dictsort:"name" }} <!-- Sorts a dictionary by the given value. -->
{{ mylist|dictsortreversed:"name" }} <!-- Sorts a dictionary reversed, by the given value. -->فیلترهای منطقی و کمکی (Logic & Utility Filters)
فیلترهای Logic & Utility در جنگو برای مدیریت شرایط خاص و سادهسازی نمایش دادهها استفاده میشوند. این فیلترها بیشتر زمانی به کار میآیند که دادهها ممکن است خالی، None یا غیرمنتظره باشند و ما بخواهیم در تمپلیت بدون نیاز به تغییر در تابع view آنها را مدیریت کنیم.
برای نمونه میتوانیم اگر مقدار یک متغیر خالی بود، بتوانیم مقدار پیشفرض نشان دهیم (default)، یا فقط در صورت None بودن مقدار جایگزین بگذاریم (default_if_none). با استفاده از yesno میتوانیم مقادیر بولی را به متن قابل فهم برای کاربر تبدیل کنیم (مثلاً True → Yes و False → No). همچنین pluralize برای جمع بستن واژهها کاربرد دارد (مانند اضافه کردن s در انگلیسی) و باعث میشود متنها از نظر دستوری صحیحتر باشند.
این فیلترها درواقع ابزارهای کمکی هستند که منطق ساده و پرکاربرد را مستقیماً در لایهی نمایش (تمپلیت) پیادهسازی میکنند.
{{ myvar|default:"No Value" }} <!-- Returns a specified value if the value is False. -->
{{ myvar|default_if_none:"Empty" }} <!-- Returns a specified value if the value is None. -->
{{ mycondition|yesno:"Yes,No" }} <!-- Converts Booleans values into specified values. -->فیلترهای مخصوص URL و JSON
فیلترهای URL & JSON در جنگو برای پردازش دادههایی به کار میروند که نیاز به استفاده در لینکها، اسکریپتها یا تبادل داده با مرورگر دارند. این فیلترها به ما کمک میکنند متنها یا مقادیر را به شکلی امن و استاندارد در آدرسها یا کدهای جاواسکریپت قرار دهیم.
برای مثال، با urlencode میتوانیم متن را به فرمت امن URL تبدیل کنیم (مثل تبدیل فاصله به + یا %20)، و با urlize یا urlizetrunc لینکهای موجود در متن را به تگهای <a> تبدیل کرده و آنها را قابل کلیک کنیم. همچنین iriencode برای کدگذاری در URIهای بینالمللی استفاده میشود. در بخش JSON هم فیلتر json_script دادهها را به صورت امن داخل یک تگ <script> قرار میدهد تا بدون مشکل امنیتی (مثل XSS) در جاواسکریپت استفاده شوند.
این فیلترها مخصوصاً در پروژههایی که تعامل زیادی با مرورگر، APIها یا دادههای داینامیک دارند بسیار حیاتی هستند.
{{ "a b c"|urlencode }} ┈┈┈⮞ a%20b%20c <!-- encodes for query strings (form data) -->
{{ "visit https://arazsx.ir"|urlize }} ┈┈┈⮞ <a href="https://arazsx.ir">https://arazsx.ir</a> <!-- Returns any URLs in a string as HTML link -->
{{ "visit https://arazsx.ir"|urlizetrunc:15 }} ┈┈┈⮞ <a href="https://arazsx.ir">https://arazsx…</a> <!-- Returns any URLs in a string as HTML links, but shortens the links into the specified number of characters. -->
{{ mydata|json_script:"my-data" }} ┈┈┈⮞ <script id="my-data" type="application/json">[{"brand": "Ford", "model": "Mustang", "year": 1964}]</script> <!-- Returns an object into a JSON object surrounded by <script></script> tags. -->فیلترهای سفارشی – Custom Filters
هرچند جنگو مجموعه کاملی از تگها و فیلترها را معرفی کرده و در اختیار توسعهگران قرار داده است، اما براساس نیازهای هر پروژه ممکن است تگ و یا فیلتری مورد نیاز باشد که در لیست تگهای معرفی شده جنگو نباشد. در اینجا جنگو امکانی را فراهم آورده که بتوان در راحتترین شکل ممکن یک فانکشن پایتون را بر اساس نیاز decorate کرد و به عنوان یک custom template filter ثبت کرد. برای این منظور داخل دایرکتوری coreapp، یک دایرکتوری جدید به نام templatetags ایجاد کرده و سپس با ایجاد صرفا یک فایل خالی از محتوا با عنوان __init__.py داخل آن، دایرکتوری templatetags را بهعنوان پکیج پایتون مطرح میکنیم.
هرچند جنگو، مجموعهای غنی و کامل از تگها و فیلترهای داخلی را در اختیار توسعهدهندگان قرار داده است، اما در پروژههای واقعی اغلب با نیازهایی مواجه میشویم که پوشش داده نشدهاند. برای پاسخگویی به این نیازها، جنگو امکانی انعطافپذیر فراهم کرده است. میتوانیم توابع پایتونی خود را به سادگی با دکوراتورهای – decorator – اختصاصی، به عنوان فیلتر یا تگ سفارشی تمپلیت ثبت نمود و مستقیماً در تمپلیتها از آنها استفاده کرد.
برای پیادهسازی این قابلیت، در اپلیکیشن مورد نظر (مثلاً coreapp)، یک دایرکتوری با نام templatetags ایجاد نموده و سپس، برای تبدیل این دایرکتوری به یک پکیج پایتون، یک فایل خالی با نام __init__.py در آن قرار میدهیم.
پس از این مرحله، جنگو قادر خواهد بود فایلهای داخل templatetags را شناسایی کرده و توابع ثبتشده در آنها را به عنوان تگ یا فیلتر در تمپلیتها در دسترس قرار دهد. این رویکرد، توسعهدهندگان را قادر میسازد تا منطقهای تکراری یا پیچیده را از توابع view خارج کرده و مستقیماً در لایه تمپلیت مدیریت کنند — بدون نقض اصول MVC و با حفظ خوانایی و قابلیت نگهداری کد.
├───coreapp
│ admin.py
│ apps.py
│ models.py
│ tests.py
│ utls.py
│ views.py
│ __init__.py
│
└───templatetags
__init__.py # Must Exist (Mandatory).
customtags.py # Main File for Custom Filter Functions' Definition
در این مرحله، فایل دیگری با نام دلخواه customtags.py داخل دایرکتوری templatetags/ ایجاد کرده تا تگها و فیلترهای سفارشی را داخل آن تعریف و استفاده نمود. ذکر این نکته حائز اهمیت است که برای استفاده از تگها و یا فیلترهای سفارشی در تمپلیت حتما میبایست از تگ {% load %} و به صورت (% load customtags %} در ابتدای تمپلیتی که قرار است از تگها و یا فیلترهای سفارشی استفاده کند، بهره گرفت.
coreapp/templatetags/customtags.py
from django import template
register = template.Library()
حال میتوانیم یک فیلتر تمپلیت سفارشی (Custom Template Filter) ایجاد کنیم که عملکردی مشابه متد str.split() در پایتون داشته باشد. این فیلتر به ما امکان میدهد رشتههای ذخیرهشده در مدل (مثلاً تگها، لیست کلمات کلیدی، یا مقادیر CSV) را مستقیماً در تمپلیتها، بر اساس یک جداکننده (delimiter) به لیستی از آیتمها تقسیم کنیم — بدون نیاز به پیشپردازش در ویوها.
from django import template
register = template.Library()
@register.filter
def split(value, delimiter = ","):
return value.split(delimeter)
برای استفاده از فیلتر سفارشی split که در فایل customtags.py تعریف نمودیم، اولین و ضروریترین گام، وارد کردن کتابخانه مربوطه در ابتدای تمپلیت مورد نظر است. این کار با استفاده از تگ {% load customtags %} انجام میشود این دستور باید فقط یکبار در هر تمپلیت قرار گیرد — حتی اگر از چندین فیلتر یا تگ موجود در customtags.py استفاده گردد. همچنین، این دستور باید قبل از استفاده از فیلترها در تمپلیت آمده باشد، در غیر این صورت با خطای Invalid filter: 'split' مواجه خواهیم شد.
نحوه استفاده از فیلتر سفارشی در تمپلیت همانند فیلترهای داخلی جنگو خواهد بود
{% extends "base.html" %}
{% load customtags %} ← This is a vital line!
{% block content %}
<h2>Post Tags</h2>
<div>
{% for tag in post.tags|split:"-" %}
<span>{{ tag|capfirst }}</span>
{% endfor %}
</div>
{% endblock content %}
━◦○◦ نکتههای تکمیلی ◦○◦━
templatetags استفاده میکنیم (مثلاً customtags.py و coretags.py)، میتوان همه را در یک خط وارد تمپلیت نمود.{% load customtags coretags %}
⮜ برای جلوگیری از تکرار، میتوان {% load customtags %} را در base.html قرار داد — در این صورت تمام تمپلیتهایی که از آن extend میکنند، بهصورت خودکار فیلترها را در دسترس خواهند داشت.