پیکربندی Forms


مقدمه


در این بخش، به بررسی سایر عملیات اساسی مدیریت داده‌ها CRUD در برنامه‌های تحت وب، یعنی نحوه‌ی ایجاد (Create)، ویرایش (Update) و حذف (Delete) داده‌های یک مدل می‌پردازیم.

در قسمت پایانی بخش قبلی، فرآیند خواندن (Read) داده‌ها را بررسی کردیم. در آن مثال، با کلیک روی نام پروژه به مسیر read-project/ هدایت می‌شدیم و جزئیات هر پروژه نمایش داده می‌شد. این فرآیند، تنها یک‌طرفه بود: داده‌ها از پایگاه داده خوانده و به کاربر نمایش داده می‌شدند، اما هیچ امکانی برای ارسال یا تغییر داده وجود نداشت.

برای پیاده‌سازی سایر عملیات CRUD که در بالا اشاره شد، نیازمند یک مکانیزم برای دریافت ورودی از کاربر و ارسال آن به سرور هستیم. در دنیای وب، این وظیفه به‌عهده‌ی فرم‌ها (Forms) است. در فریم‌ورک جنگو نیز، فرم‌ها نقش کلیدی در انتقال داده‌ها بین تمپلیت‌ها (صفحات HTML) و پایگاه داده ایفا می‌کنند.


مفهوم فرم در HTML

در HTML، یک Form شامل مجموعه‌ای از فیلدها یا ویجت‌ها (widgets) است که برای دریافت داده از کاربر و ارسال آن به سرور استفاده می‌شود. فرم‌ها ابزارهایی انعطاف‌پذیر هستند که امکان جمع‌آوری انواع داده‌ها را از طریق المان‌هایی مانند text box، checkbox، radio button، انتخاب‌گر تاریخ و... فراهم می‌کنند.

علاوه بر جمع‌آوری داده، فرم‌ها می‌توانند داده‌ها را در قالب درخواست‌های POST ارسال کنند و از طریق مکانیزم‌های امنیتی مانند توکن CSRF (Cross-Site Request Forgery protection) که امنیت نسبی بالایی دارند از حملات امنیتی رایج جلوگیری می‌کنند. این توکن به‌صورت خودکار توسط جنگو در فرم‌ها قرار داده می‌شود و اطمینان حاصل می‌کند که درخواست‌ها واقعا از طرف کاربر معتبر ارسال شده‌اند.

اگرچه تاکنون به‌صورت مستقیم فرمی ایجاد نکرده‌ایم، اما در رابط مدیریت جنگو (Admin Interface) هنگام افزودن پروژه‌ها، در واقع در پس‌زمینه از یک فرم مبتنی بر مدل Project استفاده می‌شد.


سیستم فرم در جنگو

جنگو با فراهم کردن یک چارچوب قدرتمند و انعطاف‌پذیر برای مدیریت فرم‌ها، اجازه می‌دهد فرم‌های دلخواه را تعریف کنیم.این سیستم نه‌تنها کد HTML مربوط به فرم را به‌صورت خودکار تولید می‌کند، بلکه مراحل اعتبارسنجی داده‌ها (Validation)، پردازش ورودی‌ها و ذخیره‌سازی ایمن در پایگاه داده را نیز تسهیل می‌کند.

اصلی‌ترین بخش این سیستم، کلاس Form است. جنگو دو نوع فرم ارائه می‌دهد

  1. Form — برای ساخت فرم‌های سفارشی که لزوما به یک مدل متصل نیستند. به‌عنوان مثال، فرم‌های تماس یا جستجو که داده‌هایشان مستقیما در پایگاه داده ذخیره نمی‌شوند.

  2. ModelForm — برای ساخت فرم‌هایی که مستقیما مبتنی بر یک مدل جنگو بوده و به‌صورت خودکار فیلدهای مدل را به المان‌های فرم تبدیل می‌کند و تمامی ویژگی‌های مرتبط — از جمله نوع ورودی (ویجت)، برچسب‌ها، مقادیر پیش‌فرض، محدودیت‌ها و پیام‌های خطا — را مدیریت می‌کند.

در اغلب مواقع، از ModelForm به‌ویژه هنگام پیاده‌سازی عملیات CRUD، از ModelForm استفاده می‌شود، چرا که توسعه را سریع‌تر، ایمن‌تر و کم‌خطا‌تر می‌کند.


تعریف یک ModelForm

تعریف ModelForm بسیار ساده است و حداقل به دو بخش نیاز دارد:

  • مدل مرجعی که فرم بر اساس آن ساخته می‌شود ← model

  • لیستی از فیلدهایی از مدل که باید در فرم نمایش داده شوند. ← fields

به‌طور متعارف، فرم‌ها در یک فایل جداگانه با نام forms.py در دایرکتوری اپلیکیشن تعریف می‌شوند. این فایل به‌صورت پیش‌فرض در ساختار پروژه‌ی جنگو وجود ندارد و باید به‌صورت دستی ایجاد شود.

 

coreapp/forms.py

from django.forms import ModelForm
from .import models

class ProjectForm(ModelForm):
    class Meta:
        model = models.Project
        fields = ['title', 'area', 'content', 'tags', ]
  • کلاس ProjectForm از ModelForm ارث‌بری می‌کند.
  • در بخش Meta، مدل مرجع (Project) و فیلدهای مورد نظر برای نمایش در فرم مشخص شده‌اند.
  • جنگو به‌صورت خودکار برای هر فیلد، المان HTML مناسب (ویجت) را بر اساس نوع داده‌ی تعریف‌شده در مدل تولید می‌کند. مثلا یک فیلد CharField به یک <input type="text"> تبدیل می‌شود، در حالی که یک TextField به یک <textarea> تبدیل می‌گردد.
💡 نکته مهم: نام کلاس فرم معمولاً با حرف بزرگ شروع می‌شود (مثلا ProjectForm) تا با قراردادهای نام‌گذاری پایتون (PascalCase) هماهنگ باشد.

به‌عبارتی، فرآیندی که در بالا جریان می‌­یابد بدین صورت است که جنگو model مورد اشاره در دستور کد را فراخوانی کرده و فرمی با فیلدهای مورد اشاره در قسمت fields مبتنی بر نوع داده­‌های (Data Types) تعریف شده در مدل می­‌سازد.

اگر بخواهیم فرمی ایجاد کنیم که همه‌ی فیلدهای مدل به جز فیلدهای غیرقابل ویرایش  را شامل شود، می‌توان از عبارت ویژه‌ی __all__ استفاده نمود. این روش به‌ویژه در مراحل اولیه‌ی توسعه یا برای مدل‌های ساده، بسیار کاربردی خواهد بود.

fields = '__all__'

نکته‌ی مهم دیگر این است که همه‌ی فیلدهای مدل، لزوما در فرم نمایش داده نمی‌شوند. به‌طور خاص:

  • فیلدهایی که دارای پارامتر editable=False هستند (مانند فیلد created و updated که معمولا به‌صورت خودکار پر می‌شود).
  • فیلدهایی که به‌صورت خودکار توسط سیستم مدیریت می‌شوند (مانند فیلد id یا فیلدهای auto_now و auto_now_add).

این فیلدها به‌طور خودکار از فرم حذف می‌شوند، چرا که جنگو فرض می‌کند کاربر نباید مستقیما روی آن‌ها تاثیر بگذارد.

جنگو علاوه بر مشخص کردن فیلدهای مورد نظر در مدل–فرم‌ها با پارامتر fields، امکان حذف انتخابی برخی فیلدها از فرم را نیز فراهم می‌کند. این کار با استفاده از پارامتر exclude در کلاس Meta انجام می‌شود. زمانی که از fields = '__all__' استفاده می‌کنیم، تمامی فیلدهای قابل ویرایش مدل در فرم ظاهر می‌شوند. اما گاهی نیاز خواهد بود که همه‌چیز را نشان دهیم، به‌جز چند فیلد خاص — مثلا فیلدی که بخواهیم به‌صورت خودکار پر شود یا فیلدی که کاربر نباید آن را تغییر دهد. در چنین مواردی، به‌جای لیست‌کردن تمام فیلدهای مورد نظر در fields، می‌توان از exclude استفاده نمود

 

coreapp/forms.py

from django.forms import ModelForm
from .import models

class ProjectForm(ModelForm):
    class Meta:
        model = models.Project
        exclude = ['owner']

 

⚠️ نمی‌توان همزمان از fields و exclude در یک ModelForm استفاده نمود. جنگو اجازه‌ی ترکیب این دو پارامتر را نمی‌دهد، چرا که ممکن است منجر به تناقض یا رفتارهای غیرمنتظره شود و حتما باید یکی از دو روش را انتخاب کرد

  • مشخص کردن فیلدهای مورد نظر با fields
  • یا مشخص کردن فیلدهای غیرمورد نظر با exclude

ایجاد داده – Create


در این بخش، نحوه ایجاد یک صفحه برای ذخیره داده جدید (مثلاً یک پروژه جدید) در پایگاه داده با استفاده از مدل–فرم‌های جنگو را به‌صورت کامل و گام‌به‌گام توضیح می‌دهیم. هدف ما این است که با استفاده از یک فرم هوشمند و ایمن، داده‌های واردشده توسط کاربر را اعتبارسنجی کرده و در صورت صحیح بودن، در پایگاه داده ذخیره کنیم.

گام اول: ایجاد View برای نمایش فرم

اولین قدم، تعریف یک View در فایل views.py خواهد بود که فرم مربوطه را به کاربر نمایش دهد.

💡 نکته: در این مرحله فقط فرم را نمایش می‌دهیم. ذخیره‌سازی داده در مرحله بعدی (با مدیریت درخواست POST) انجام می‌شود.

coreapp/views.py

from . import forms

 def ProjectCreate(request):

    projectForm = forms.ProjectForm()
    context = {'form': projectForm }
    return render(request, 'forms.html', context)

 

گام دوم: تعریف مسیر (URL) مربوط به View

حال باید یک الگوی مسیر (URL) برای دسترسی به View ایجاد شده، در فایل urls.py تعریف گردد.

coreapp/urls.py

urlpatterns = [
    ...,
    path('project-add/', views.ProjectCreate, name='ProjectCreate'),
]

 

 گام سوم: ایجاد تمپلیت عمومی برای فرم‌ها

در مرحله پایانی به سراغ ایجاد تمپلیت عمومی forms.html در دایرکتوری ریشه پروژه خواهیم رفت. یکی از مزیت‌های جنگو این است که می‌توان یک تمپلیت عمومی برای تمام فرم‌های ایجاد (Create) و ویرایش (Update) طراحی نمود. که تمامی مدل‌ها را پوشش دهد. این کار از تکرار کد جلوگیری کرده و نیاز به ایجاد تمپلیت‌های فرم متعدد برای مدل‌های مختلف را — مگر در مواردی خاص — از بین می‌برد.


tutorial/templates/forms.html

{% extends 'base.html' %}

{% block content %}
<form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Save</button>
</form>
{% endblock %}


جنگو به‌صورت پیش‌فرض از CSRF (Cross-Site Request Forgery) محافظت می‌کند. تگ {% csrf_token %} یک توکن امنیتی در فرم قرار می‌دهد که بدون آن، جنگو هر درخواست POST را رد کرده و خطای 403 Forbidden نمایش می‌دهد. همواره در هر فرمی که از method="POST" استفاده می‌کند، می‌بایست تگ {% csrf_token %} را  قرار دهیم.

هنگام استفاده از تگ {{ form.as_p }}، جنگو به‌صورت خودکار تمام فیلدهای فرم را به‌صورت یک سری تگ <p> (پاراگراف) رندر می‌کند. هر فیلد در یک تگ <p> جداگانه قرار می‌گیرد که شامل برچسب (label) و ورودی (input) فیلد مربوطه خواهد بود و زمانی کاربردی خواهد بود که بخواهیم سریعا یک فرم قابل استفاده داشته باشیم (مثلا در مرحله توسعه یا پروتوتایپ) و نیازی به کنترل دقیق روی ظاهر فرم نداشته باشیم.

پس از اتمام مراحل، می‌توان نمایشی از فیلد­ها را برای ایجاد پروژه جدید با رجوع به آدرس project-add/ در مرورگر، مشاهده کرد.

در پروژه‌های نهایی (production) که ظاهر کاربری مهم است و یا زمانی که نیاز به نمایش پیغام‌های خطا (Errors)، متن‌های راهنما (Help Text) یا استایل‌دهی پیشرفته داشته باشیم، بهتر است فرم عمومی forms.html را به‌صورت دستی و با بهره‌گیری از حلقه رندر کنیم.

tutorial/templates/forms.html

{% extends 'base.html' %}
{% block content %}
  <form method="POST">
    {% csrf_token %}    
    {% for field in form %}
      <div class="mb-3">
        {{ field.label_tag }}
        {{ field }}
        {% if field.help_text %}
            <small>{{ field.help_text }}</small>
        {% endif %}
        {% if field.errors %}
            <div>{{ field.errors }}</div>
        {% endif %}
      </div>
    {% endfor %}
    <button type="submit">Save</button>  
  </form>
{% endblock %}

 

تا به اینجا، تنها به نمایش تمپلیت فرم برای ایجاد پروژه در مدل Project توسط مدل–فرم ProjectForm پرداختیم. حال در ادامه می‌­بایست به پردازش داده‌های ارسالی از طریق فرم و ذخیره آن‌ها در پایگاه داده بپردازیم که یکی از هسته‌های اصلی کار با فرم‌ها در Django است

 

گام چهارم: پردازش و ذخیره‌سازی داده‌ها

 

    if request.method == 'POST':
      data = forms.ProjectForm(request.POST)
      if data.is_valid():
          data.save()
          return redirect('Projects')

 

— بررسی نوع درخواست

وقتی کاربر روی Submit فرم کلیک می‌کند، مرورگر یک درخواست HTTP از نوع POST به سرور ارسال می‌کند.. در view می‌بایست در ابتدای کار نوع درخواست ارسالی را توسط if request.method == 'POST' بررسی نمود. این بررسی، اولین سطح از منطق کنترل جریان پردازش و دخیره‌سازی داده‌ها در View می‌باشد.

  • request.method نشان می‌دهد که این درخواست از چه نوعی است (GE
  • فقط زمانی داده‌ها پردازش خواهند شد که درخواست از نوع POST باشد (یعنی کاربر، داده ارسال نموده باشد).
  • T، POST، PUT و غیره).اگر درخواست از نوع GET باشد (مثلا وقتی کاربر صفحه را برای اولین بار باز می‌کند)، این بلوک اجرا نمی‌شود و معمولا یک فرم خالی نمایش داده می‌شود.

 

 ایجاد نمونه فرم با داده‌های ارسالی

در این مرحله با دستور formData = ProjectForm(request.POST) فقط داده‌ها را دریافت و به فرم نسبت می‌دهیم — هنوز هیچ اعتبارسنجی یا ذخیره‌سازی انجام نشده است.
  • ProjectForm یک کلاس فرم است که از forms.ModelForm ارث‌بری کرده و مربوط به مدل Project است.
  • request.POST یک دیکشنری‌ مانند (QueryDict) بوده که تمام داده‌های ارسالی از طریق فرم (با متد POST) را در خود دارد.
  • با ارسال request.POST به سازنده فرم ProjectForm(...), جنگو یک نمونه پر شده از فرم ایجاد می‌کند که داده‌های واردشده توسط کاربر را در خود نگه می‌دارد.
 

 اعتبارسنجی داده‌ها

اعتبارسنجی داده‌ها با متد is_valid() یکی از قدرتمندترین ویژگی‌های جنگو است چرا که از خطاهای انسانی (مثل وارد کردن ایمیل نامعتبر) و حملات امنیتی (مثل تزریق داده — Data Injection ) جلوگیری می‌کند.

  • تمام فیلدهای فرم را بر اساس قوانین تعریف‌شده در مدل یا فرم (مثل max_length، required، validators و غیره) بررسی می‌کند.
    • اگر همه داده‌ها معتبر باشند:
      • مقدار True برگردانده می‌شود.
      • داده‌های تمیزشده (cleaned data) در ویژگی formData.cleaned_data قرار می‌گیرند.
    • اگر حتی یک فیلد نامعتبر باشد:
      • مقدار False برگردانده می‌شود.
      • خطاهای مربوطه در formData.errors ذخیره می‌شوند و می‌توان آن‌ها را در تمپلیت نمایش داد.
 

 ذخیره داده در پایگاه داده

اگر فرم معتبر باشد، از آنجا که ProjectForm یک ModelForm است، متد save() به‌صورت خودکار:

  • یک رکورد — Object — جدید از مدل Project ایجاد می‌کند.
  • مقادیر فیلدها را از cleaned_data به آن Object اختصاص می‌دهد.
  • Object را با فراخوانی save()، در پایگاه داده ذخیره می‌کند (یک رکورد جدید در جدول project ایجاد می‌شود).
💡 اگر فرم از forms.Form (نه ModelForm) ارث‌بری کرده بود، متد save() دیگر وجود نخواهد داشت و باید به‌صورت دستی داده‌ها را در مدل ذخیره نمود.
 

 هدایت مجدد کاربر

پس از ذخیره موفقیت‌آمیز داده، به‌جای بازگرداندن یک صفحه جدید یا نمایش پیام، می‌توان کاربر را به یک URL دیگر هدایت کرد. این کار با بهره‌گیری از متد redirect , بر اساس الگوی طراحی معروف PRG (Post-Redirect-Get) انجام می‌شود.

چرا این کار مهم است؟

  • جلوگیری از ارسال مجدد تصادفی فرم (اگر کاربر F5 بزند، دوباره داده ذخیره نمی‌شود).
  • رفتار استاندارد و امن در وب.
  • بهبود تجربه کاربری (مثلا هدایت به لیست پروژه‌ها پس از ایجاد یک پروژه جدید).
⚠️ در متد redirect از مقداری که برای متغیر name — در اینجا name="Projects" — برای الگوی URL در لیست urlpatterens=[] تعریف شده، برای فراخوانی URL استفاده می‌گردد.

⚠️ برای استفاده از redirect، می‌بایست متد آن را از کتابخانه django.shortcuts وارد نمود

 
درنهایت، view مربوط به ایجاد داده به‌صورت زیر خواهد بود.
coreapp/views.py
from django.shortcuts import render, redirect

def ProjectCreate(request):

    projectForm = forms.ProjectForm()

    if request.method == 'POST':
      data = forms.ProjectForm(request.POST)
      if data.is_valid():
          data.save()
          return redirect('Projects')

    context = {'form': projectForm }
    return render(request, 'forms.html', context)

 

⚠️ برای نمایش خطاها در صورت نامعتبر بودن فرم، می‌بایست حتما فرم را دوباره به تمپلیت برگردانیم.
    if request.method == 'POST':
      data = forms.ProjectForm(request.POST)
      if data.is_valid():
          ...
      else:
        projectForm = forms.ProjectForm()

    context = {'form': projectForm }
    return render(request, 'forms.html', context)
 
 

⚠️ هرگاه فرم شامل آپلود فایل باشد، باید request.FILES را هم به فرم اضافه نماییم 

data = ProjectForm(request.POST, request.FILES)
 
در این‌صورت تمپلیت فرم هم می‌بایست گزینه enctype="multipart/form-data" را در اختیار داشته باشد.
<form class="" action="" method="POST" enctype="multipart/form-data"> 
    ...
</form>
 

ویرایش داده – Update


فرآیند ویرایش (Update) داده در جنگو بسیار شبیه به فرآیند ایجاد (Create) است، با این تفاوت که در هنگام نمایش فرم ویرایش، فیلدهای آن از قبل با داده‌های موجود در پایگاه داده پر شده‌اند. این امر به کاربر اجازه می‌دهد تا داده‌های قبلی را مشاده کرده و در صورت نیاز، آن‌ها را تغییر دهد.

برای پیاده‌سازی این عملکرد، نیاز است تا یک شناسه منحصربه‌فرد (مانند id) از رکورد مورد نظر دریافت شود تا بتوان دقیقا همان رکورد را از پایگاه داده بازیابی کرده و در تمپلیت فرم نمایش داد. در جنگو، هر مدل به‌صورت پیش‌فرض یک فیلد id از نوع AutoField دارد که منحصربه‌فرد (unique) و غیرقابل تغییر است — در مدل Project فیلد id از نوع UUIDField تعریف شد — از فیلد id به‌عنوان شناسه داده جهت پیاده‌سازی فرایند ویرایش استفاده خواهیم نمود.

در فایل views.py، یک view با عنوان ProjectUpdate تعریف می‌کنیم که مسئول نمایش فرم ویرایش و ذخیره تغییرات خواهد بود

coreapp/views.py

def ProjectUpdate(request, uid):
    projectObj = models.Project.objects.get(id=uid)
    projectData = forms.ProjectForm(instance=projectObj)

    if request.method == 'POST':
        data = forms.ProjectForm(request.POST, instance=projectObj)
        if data.is_valid():
            data.save()
            return redirect('Projects')
        else:
            projectData = data

    context = { 'form': projectData }
    return render(request, 'forms.html', context)

استفاده از پارامتر instance=projectObj هنگام ایجاد فرم، باعث می‌شود فیلدهای فرم با مقادیر موجود در آن رکورد از پایگاه داده پر شوند. همچنین، هنگام ذخیره‌سازی (data.save())، جنگو به‌جای ایجاد رکورد جدید، همان رکورد قبلی را به‌روزرسانی می‌کند.

 

برای دسترسی به view، باید یک الگوی URL آن را در فایل urls.py تعریف کنیم که شناسه (uid) را به عنوان پارامتر دریافت کند

coreapp/urls.py

urlpatterns = [
    ... ,
    path(project-edit/<str:uid>/', views.ProjectUpdate, name='ProjectUpdate'),
]
  • از <str:uid> استفاده کردیم چون id یک مقدار رشته‌ای تصادفی است. این کار هم امنیت بیشتری فراهم می‌کند و هم از خطاهای نوع داده جلوگیری می‌کند.
  • تعیین عنوان مسیر (name='ProjectUpdate') برای استفاده در تمپلیت‌ها با تگ {% url %} ضروری است.

 

در تمپلیت نمایش جزئیات پروژه (در اینجا read-project.html)، یک لینک ویرایش اضافه می‌کنیم که شناسه پروژه را به مسیر ویرایش منتقل کند.

<a href="{% url 'ProjectUpdate' project.id %}"> Edit Project </a>
  • این لینک، کاربر را به آدرسی همانند /project-edit/550b6632-e552-4ffe-8762-62af20db79ec/ هدایت می‌کند، که در آن view مربوطه، پروژه با id=550b6632-e552-4ffe-8762-62af20db79ec بارگذاری و در فرم نمایش داده می‌شود.
  • تعیین عنوان مسیر (name='ProjectUpdate') برای استفاده در تمپلیت‌ها با تگ {% url %} ضروری است.

 

بدین ترتیب در این فرایند، کاربر به لیست پروژه‌ها (/projects/) رفته و روی عنوان یک پروژه کلیک نموده و به صفحه جزئیات (/read-project/...) منتقل می‌شود. سپس در آنجا، بر روی دکمه "Edit Project" کلیک کرده و به صفحه‌ای با آدرس /project-edit/<id>/ هدایت می‌شود که فرمی با داده‌های فعلی پروژه نمایش داده خواهد شد. در نهایت پس از اعمال تغییرات و ارسال فرم، داده‌ها به‌روزرسانی شده و کاربر به لیست پروژه‌ها بازمی‌گردد.

 

حذف داده – Delete


برای حذف یک رکورد از مدل در جنگو، مطابق روال استاندارد، ابتدا باید یک تابع (view) اختصاصی در فایل views.py ایجاد کنیم. همانند عملیات ویرایش، فرآیند حذف نیز همیشه مربوط به یک رکورد خاص است؛ بنابراین نیاز داریم تا یک شناسهٔ منحصربه‌فرد — معمولا فیلد id — را به‌عنوان پارامتر ورودی دریافت کنیم. این فیلد به‌صورت پیش‌فرض در هر مدل Django وجود دارد، مقدار آن منحصربه‌فرد (unique=True) بوده و پس از ایجاد قابل تغییر نیست.

 

در فایل coreapp/views.py، تابعی به نام ProjectDelete تعریف می‌کنیم که مسئول بازیابی و حذف پروژه مورد نظر از پایگاه داده خواهد بود.

coreapp/views.py

def ProjectDelete(request, uid):
  
projectObj = models.Project.objects.get(id = uid)

  if request.method == 'POST':
    projectObj.delete()
    return redirect('Projects')
  • عملیات حذف فقط در پاسخ به درخواست POST انجام می‌شود تا از حذف تصادفی یا از طریق لینک ساده (مثلا با کلیک یا پیمایش خودکار) جلوگیری شود. 

 

مطابق روال همیشگی، برای دسترسی به view، یک الگوی URL تعریف می‌کنیم که شناسه پروژه (uid) را به‌عنوان پارامتر دریافت کند.

coreapp/urls.py

urlpatterns = [
    ...,
    path('project-delete/<str:uid>/', views.ProjectDelete, name='ProjectDelete'),
]

 

در تمپلیت نمایش جزئیات پروژه (read-project.html)، یک فرم و گزینه حذف تعریف می‌کنیم که با بهره‌گیری از ویژگی action فرم، شناسه پروژه را برای حذف به الگوی مسیر تعریف شده هدایت خواهد کرد.

coreapp/templates/project-read.html

<form action="{% url 'ProjectDelete' project.id %}" method="post">
  {% csrf_token %}
  <button type="submit">Delete</button>
</form>
  • حضور تگ {% csrf_token %} برای جلوگیری از حملات CSRF (Cross-Site Request Forgery) الزامی است. بدون آن، درخواست POST توسط جنگو رد خواهد شد.
  • ویژگی action در تگ <form> مشخص می‌کند که داده‌های فرم (وقتی کاربر روی دکمه type="submit" کلیک می‌کند) به کجا ارسال شوند. در صورتی که آدرسی برای ویژگی action فرم تعیین نشده باشد - action="" - باشد یا وجود نداشته باشد (<form method="post">)، داده‌ها به همان آدرسی که فرم در آن قرار دارد ارسال خواهند شد.

 

فرآیند حذف از دید کاربر به این صورت خواهد بود که، کاربر به لیست پروژه‌ها (/projects/) مراجعه کرده و بر روی عنوان یک پروژه کلیک کرده و به صفحه جزئیات آن (/read-project/...) هدایت می‌شود. در آن صفحه، با انتخاب گزینه حذف «Delete»، یک درخواست POST به آدرس /project-delete/<id>/ ارسال شده و پروژه از پایگاه داده حذف و کاربر به لیست پروژه‌ها بازمی‌گردد.