در این روش که به آن توسعه با قالبهای جنگو (Django Template Engine) یا رندر سمت سرور (Server-Side Rendering) میگویند، فرانتاند و بکاند از هم جدا نیستند. جنگو مستقیماً دادهها را از دیتابیس میگیرد و درون فایلهای HTML که با سینتکس خاص خودش (مثل {{ variable }} و {% tag %}) نوشته شدهاند، جایگذاری میکند. نتیجه نهایی یک صفحه HTML کامل است که به مرورگر کاربر فرستاده میشود. در این معماری، نیازی به API جداگانه یا فریمورک فرانتاند نیست و همه چیز در یک پروژه یکپارچه انجام میشود. به این ترتیب، منطق نمایش و دسترسی به داده کاملاً در سمت سرور مدیریت میشود و کاربر فقط نتیجه نهایی را میبیند.
در این بخش با استفاده از روشی که توضیح داده شد و دانشی که از html/css/js در بخشهای قبلی به دست آوردیم، فرانت یک پروژه واقعی را تکمیل میکنیم. بدین منظور مراحل زیر را دنبال خواهیم کرد.
- ابتدا با پروژه موجود و امکاناتش آشنا خواهیم شد.
- سپس امکاناتی که باید توسعه داده شود را شرح خواهم داد.
- و در نهایت دست به کد میشویم.
راه اندازی پروژه
ابتدا با کلیک روی دکمه زیر، بکند پروژه را دانلود کنید.

سپس با cmd یا Terminal وارد پوشه ای که فایل manage.py در آن قرار دارد شوید. سپس با استفاده از دستور زیر، یک محیط مجازی بسازید.
python3 -m venv env
py -m venv env
سپس با دستور زیر آن را فعال کنید.
source env/bin/activate
env\Scripts\activate
سپس با استفاده از دستور زیر، پکیج های مورد نیاز را نصب کنید.
pip install -r requirements.txt
سپس با استفاده از دستور زیر، دیتابیس را درست کنید.
python manage.py migrate
py manage.py migrate
سپس با دستور زیر، تعدادی اخبار فیک در دیتابیس وارد کنید.
python manage.py import_news
py manage.py import_news
سپس با استفاده از دستور زیر پروژه را اجرا کنید.
python manage.py runserver
py manage.py runserver
در صورتی که نیاز به ورود به داشبورد ادمین جنگو را داشتید از دستور زیر برای ساخت یوزر استفاده کنید. سپس میتوانید با استفاده از نام کربری و پسوردی که مشخص کردید در آدرس localhost:8000/admin لاگین کنید.
python manage.py createsuperuser
py manage.py createsuperuser
آشنایی با پروژه
این پروژه یک پایگاه خبری است که بکند آن با جنگو توسعه داده شده است. این بخش برای کسانی که با مقدمات جنگو آشنا باشند راحت تر خواهد بود اما اگر دانشی از جنگو ندارید نگران نباشید زیرا بخش هایی که با جنگو تعامل دارد به شدت ساده هستند.
این پروژه ۵ بخش اصلی دارد. برخی تکمیل شده و برخی نیازمند تکمیل هستند.
۱. صفحه لیست اخبار (صفحه اصلی)
صحفه ای برای نمایش لیست همه اخبار با دکمه های صفحه بندی برای مشاهده اخبار بیشتر. این صفحه در آدرس زیر در دسترس است.
۲. صفحه جزئیات خبر
صفحه ای برای مشاهده جزئیات خبر و دکمه های ویرایش و حذف خبر. اگر id یک خبر برابر 350 باشد، برای دسترسی به این صفحه میتوان از آدرس زیر استفاده کرد.
۳. صفحه افزودن خبر
صفحه ای با یک فرم برای افزودن خبر که هنوز توسعه آن کامل نشده و در حال حاضر خالی است. این صفحه در آدرس زیر در دسترس است.
۴. صفحه ویرایش خبر
صفحه ای با یک فرم برای ویرایش خبر که هنوز توسعه آن کامل نشده و در حال حاضر خالی است. اگر id یک خبر برابر 350 باشد، برای دسترسی به این صفحه میتوان از آدرس زیر استفاده کرد.
۵. حذف خبر
برای حذف خبر صفحه ای وجود ندارد ولی اگر id یک خبر مثلا 350 باشد و با متد POST به آدرس زیر ارسال شود، آن خبر حذف خواهد شد.
localhost:8000/news/delete/350
تسک اول – افزودن فرم جستجو و فیلتر به صفحه اصلی
شرح تسک اول
یک فرم جستجو و فیلتر مشابه تصویر زیر بسازید.

برای عملیات جستجو عبارت search و برای عملیات فیلتر عبارت category را با متد GET به آدرس همین صفحه ارسال کنید. مقادیر مجاز برای category به شرح زیر است.
- Cultural
- Political
- Sports
- Scientific
انجام تسک اول
صفحه index.html را باز کرده و کد های زیر را در خط ۱۴ آن قرار دهید.
<form class="toolbar" method="get">
<input type="text" name="search" class="search-input" placeholder="Search by title...">
<select name="category" class="filter-select">
<option value="">All Categories</option>
<option value="cultural">Cultural</option>
<option value="political">Political</option>
<option value="sports">Sports</option>
<option value="scientific">Scientific</option>
</select>
<button type="submit" class="apply-btn">🔍 Apply Filters</button>
</form>در کد بالا:
- خط ۱۴
- از آنجایی که در شرح تسک گفته شده بود که اطلاعات با متد GET به سمت بکند ارسال شود، ویژگی method را برای این فرم تنظیم نکردهایم زیرا این ویژگی به صورت پیشفرض برابر
method="get"است. - از آنجایی که در توضیحات تسک گفته شده بود که فرم را به آدرس همین صفحه ارسال کنید، ویژگی action را برای این فرم تنظیم نکردهایم زیرا مقدار پیشفرض آن
action=""است و این به معنی ارسال اطلاعات فرم به صفحه ای است که همین حالا در آن قرار دارد.
- از آنجایی که در شرح تسک گفته شده بود که اطلاعات با متد GET به سمت بکند ارسال شود، ویژگی method را برای این فرم تنظیم نکردهایم زیرا این ویژگی به صورت پیشفرض برابر
- خط ۱۵: برای این فیلد
name="search"تنظیم شده تا وقتی فرم ثبت شد، مقدار این فیلد با کلید search به سمت بکند ارسال شود. - خط ۱۶ تا ۲۲: برای فیلد select ویژگی
name="category"را تنظیم کردهایم تا وقتی فرم ثبت شد، value ی option انتخاب شده، با کلید category به سمت بکند ارسال شود. - خط ۲۳: با قرار دادن
type="submit"برای این فیلد، کاری کردهایم که وقتی روی آن کلیک شد، اطلاعات فرم به سمت بکند ارسال شود.
تسک دوم – توسعه امکان حذف خبر
شرح تسک دوم
یک دکمه حذف در صفحه ویرایش خبر قرار دارد. امکانی فراهم کنید که وقتی کاربر روی آن کلیک کرد، خبر حذف شود.
انجام تسک دوم
صفحه news-detail.html را باز کنید و کد های زیر را به جای کد های خط ۳۳ تا ۳۶ قرار دهید.
<div style="margin-top: 1.5rem; display: flex; gap: 0.5rem; align-items: center;">
<!-- Delete form (POST) -->
<form action="http://localhost:8000/news/delete/{{ article.id }}/" method="post" style="display: inline;">
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">
<button type="submit" class="btn btn-danger">🗑 Delete This Article</button>
</form>
<!-- Edit link -->
<a href="{% url 'edit_news' article.id %}" class="btn detail-btn">Edit</a>
</div>در این کد:
- خط ۳۵
- آدرسی که باید اطلاعات فرم به آن ارسال شود را با تنظیم action مشخص کردهایم.
- با قرار دادن
method="post"مشخص کردهایم که اطلاعات با متد POST به سمت بکند ارسال شود.
- خط ۳۶: جنگو در راستای افزایش امنیت، انتظار دارد که همه فرمهایی که با متد POST ثبت میشوند، یک فیلد مخفی به شکلی که در این خط تعریف شده داشته باشند. اینکه این فیلد چیست و چطور به افزایش امنیت کمک میکند موضوع این دوره آموزشی نیست به همین دلیل از شرح آن صرف نظر میکنم. برای سهولت بیشتر، جنگو امکانی فراهم کرده که میتوان این خط را با
{% csrf_token %}جایگزین کرد. - خط ۳۷: یک دکمه برای ثبت فرم قرار داده شده است.
با توجه به نکته ای که در مورد csrf_token گفته شد، میتوان کد بالا را به شکل زیر بهبود داد.
<div style="margin-top: 1.5rem; display: flex; gap: 0.5rem; align-items: center;">
<!-- Delete form (POST) -->
<form action="http://localhost:8000/news/delete/{{ article.id }}/" method="post" style="display: inline;">
{% csrf_token %}
<button type="submit" class="btn btn-danger">🗑 Delete This Article</button>
</form>
<!-- Edit link -->
<a href="{% url 'edit_news' article.id %}" class="btn detail-btn">Edit</a>
</div>تسک سوم – توسعه صفحه افزودن خبر
شرح تسک سوم
فرمی برای افزودن خبر در add-edit-news.html توسعه دهید طوری که خروجی نهایی شبیه تصویر زیر باشد. بدین منظور از اطلاعاتی که در ادامه در اختیارتان قرار گرفته استفاده کنید.

اطلاعاتی که باید در قالب فرم در این صفحه با متد POST به بکند ارسال شود شامل موارد زیر است.
| نام فیلد | مقدار مورد انتظار | اجباری | توضیح |
|---|---|---|---|
title | رشته | بله | عنوان خبر |
description | رشته (بلند) | بله | متن کامل خبر |
publish_date | رشته با فرمت YYYY-MM-DD | بله | تاریخ انتشار |
category | یکی از کلیدهای دستهبندی | بله | دستهبندی انتخابشده (cultural, political, sports, scientific) |
saveAsDraft | رشته ‘on’ در صورت تیک خوردن | خیر | اگر چکباکس فعال باشد خبر بهعنوان پیشنویس ذخیره میشود. |
image | فایل (اختیاری) | خیر | تصویر خبر؛ در ویرایش در صورت ارسال نشدن، تصویر قبلی باقی میماند. |
انجام تسک سوم
فایل add-edit-news.html را باز کنید و خط ۱۱ آن را با کد زیر جایگزین کنید.
<h2>📝 Add New Article</h2>
<form method="post" enctype="multipart/form-data">
<div class="form-grid">
<div class="form-group full-width">
<label for="title">Title *</label>
<input type="text" id="title" name="title" placeholder="Enter news title" required>
</div>
<div class="form-group full-width">
<label for="desc">Description *</label>
<textarea id="desc" name="description" placeholder="Full article text..." required></textarea>
</div>
<div class="form-group full-width">
<label for="img">Image</label>
<input type="file" id="img" name="image" accept="image/*">
</div>
<div class="form-group">
<label for="date">Publish Date *</label>
<input type="date" id="date" name="publish_date" required>
</div>
<div class="form-group">
<label for="category">Category *</label>
<select id="category" name="category" required>
<option value="">-- Select --</option>
<option value="cultural">Cultural</option>
<option value="political">Political</option>
<option value="sports">Sports</option>
<option value="scientific">Scientific</option>
</select>
</div>
<div class="form-group full-width">
<label class="checkbox-wrap">
<input type="checkbox" id="draft" name="saveAsDraft">
Save as Draft
</label>
</div>
</div>
{% csrf_token %}
<div class="form-actions">
<button type="submit" class="btn btn-primary">💾 Save Article</button>
<a href="/" class="btn btn-outline">Cancel</a>
</div>
</form>در کد بالا:
- خط ۱۲
- با قرار دادن
method="post"امکان ارسال اطلاعات با متد POST را فراهم کردهایم. - با قرار دادن
enctype="multipart/form-data"امکان ارسال فایل را فراهم کردهایم.
- با قرار دادن
- خط ۱۶: با قرار دادن
name="title"عنوان خبر را با کلید name به سمت بکند ارسال میکنیم. - خط ۲۰: با قرار دادن
name="description"توضیحات خبر را با کلید description به سمت بکند ارسال میکنیم. - خط ۲۴
- با قرار دادن
type="file"یک فیلد آپلود فایل ایجاد کردهایم. - با قرار دادن
name="image"فایل را با کلید image به سمت بکند ارسال میکنیم. - با قرار دادن
accept="image/*"فقط تصاویر را مجاز به انتخاب میکنیم.
- با قرار دادن
- خط ۲۸
- با قرار دادن
type="date"ویجت انتخاب تاریخ (از امکانات HTML5) را برای این فیلد فعال کردهایم. - با قرار دادن name=”publish_date”، تاریخ را با کلید publish_date به سمت بکند ارسال میکنیم.
- با قرار دادن
- خط ۳۲: در این بخش با استفاده از select یک منو برای انتخاب دسته خبر درست کردهایم و گزینه های این منو را با استفاده از option ایجاد کرده ایم.
- با قرار دادن
name="category"مقدار انتخابی را با کلید category به سمت بکند ارسال میکنیم. - هر option ای که هنگام ثبت فرم انتخاب شده باشد، مقدار value اش با کلید category به بکند ارسال میشود.
- با قرار دادن
- خط ۴۲: با قرار دادن
type="checkbox"یک اینپوت از نوع چکباکس تعریف کرده ایم که اگر تیک آن خورده باشد، مقدار on را به بکند ارسال میکند. - خط ۴۷: یک فیلد مخفی برای ارسال توکن امنیتی به بکند در نظر گرفتهایم.
- خط ۴۹: با قرار دادن
type="submit"یک دکمه برای ثبت فرم و ارسال اطلاعات به بکندایجاد کردهایم.
تسک چهارم – توسعه فرم ویرایش خبر
شرح تسک چهارم
امکان ویرایش خبر را به فرمی که قبلا برای افزودن خبر توسعه داده اید اضافه کنید. بدین منظور از اطلاعات زیر که از سمت بکند در این صفحه در اختیار شما گذاشته شده است استفاده کنید.
| متغیر | نوع | توضیح |
|---|---|---|
article | شیء NewsArticle یا None | در حالت ویرایش حاوی اطلاعات خبر است؛ در غیر اینصورت None میباشد. |
categories | لیستی از تاپلهای (کلید، برچسب) | گزینههای دستهبندی؛ مثال: [(‘cultural’,’cultural’), (‘political’,’political’), …] |
انجام تسک چهارم
صفحه add-edit-news.html را باز کرده و کد های زیر را در آن قرار دهید.
<h2>📝 {% if article.title %}{{ article.title }}{% else %}Add New Article{% endif %}</h2>
<form method="post" enctype="multipart/form-data">
<div class="form-grid">
<div class="form-group full-width">
<label for="title">Title *</label>
<input type="text" id="title" name="title" placeholder="Enter news title" required value="{{ article.title }}">
</div>
<div class="form-group full-width">
<label for="desc">Description *</label>
<textarea id="desc" name="description" placeholder="Full article text..." required>{{ article.description }}</textarea>
</div>
<div class="form-group full-width">
<label for="img">Image</label>
<input type="file" id="img" name="image" accept="image/*">
</div>
<div class="form-group">
<label for="date">Publish Date *</label>
<input type="date" id="date" name="publish_date" required value="{{ article.publish_date|date:'Y-m-d' }}">
</div>
<div class="form-group">
<label for="category">Category *</label>
<select id="category" name="category" required>
<option value="">-- Select --</option>
<option value="cultural" {% if article.category == "cultural" %}selected{% endif %}>Cultural</option>
<option value="political" {% if article.category == "political" %}selected{% endif %}>Political</option>
<option value="sports" {% if article.category == "sports" %}selected{% endif %}>Sports</option>
<option value="scientific" {% if article.category == "scientific" %}selected{% endif %}>Scientific</option>
</select>
</div>
<div class="form-group full-width">
<label class="checkbox-wrap">
<input type="checkbox" id="draft" name="saveAsDraft" {% if article.is_draft %}checked{% endif %}>
Save as Draft
</label>
</div>
</div>
{% csrf_token %}
<div class="form-actions">
<button type="submit" class="btn btn-primary">💾 Save Article</button>
<a href="/" class="btn btn-outline">Cancel</a>
</div>
</form>در کد بالا:
- خط ۱۱: در این بخش با افزودن یک شرط، امکان نمایش عنوان درست این بخش را فراهم کردهایم. اگر در حالت افزودن باشد کاربر عبارت Add New Article و اگر در حالت ویرایش باشد، عنوان خبر را میبیند.
- خط ۱۶: با استفاده از
value="{{ article.title }}"مقدار اولیه این فیلد را مشخص کردهایم. - خط ۲۰: با قرار دادن
{{ article.description }}بین textarea مقدار اولیه این فیلد را مشخص کردهایم. - خط ۲۸: در این بخش با قرار دادن
value="{{ article.publish_date|date:'Y-m-d' }}"تاریخ ثبت خبر را با فرمت YYYY-MM-DD به مرورگر میدهیم و مقدار اولیه اولیه این فیلد را تنظیم میکنیم. - خط ۳۴ تا ۳۷: در این بخش، مقدار selected را برای آن دستهبندی ای که قبلا انتخاب شده، قرار میدهیم.
- خط ۴۲: در این بخش اگر مقدار این فیلد قبلا تیک خورده باشد، مقدار checked برایش تنظیم میشود.
در این بخش مقدار پیشفرضی برای File Upload Field قرار ندادهایم زیرا مرورگر به دلایل امنیتی اجازه این کار را نمیدهد.
به دلیل اجتناب از پیچیدگیهایی که ما را از هدف آموزشی این دوره دور میکند، از پیاده سازی امکان ویرایش تصویر در این فرم صرف نظر میکنیم.
چالش های این روش
توسعهٔ فرانت با قالبهای سنتی جنگو عملاً برای یک فرانتاند کار جذابی نیست. او مجبور میشود بخش زیادی از جنگو را یاد بگیرد؛ چیزی که برایش هیچ مزیت شغلی یا فنی ندارد و طبیعی است که تمایلی به آن نشان ندهد. از همه مهمتر، این روش دست او را از امکانات مدرن و پیشرفته (مثل کامپوننتها، مدیریت state، Hot Reload و باندلرهای بهینهساز) کوتاه میکند و کار توسعه را برایش سختتر میسازد.
برای یک بکاند کار هم این رویکرد منطقی نیست. هیچ توسعهدهندهٔ بکاندی دلش نمیخواهد تا این حد با HTML، CSS و جزئیات رابط کاربری درگیر شود. آنها ترجیح میدهند بهجای دستوپنجه نرم کردن با لایهٔ نمایش، یک API استاندارد و تمیز طراحی کنند و ادامهٔ ماجرا را بسپارند به فریمورکهای تخصصی فرانت.
در بخش بعدی خواهیم دید که چطور بدون تعامل با لایه template های جنگو و صرفا با استفاده از html/css/js میتوان این پروژه را تکمیل کرد. این شیوه برای یک فرانتاند کار بسیار جذاب تر از روشی است که در این بخش استفاده کردیم.