مقدمه


در این بخش، به بررسی سایر عملیات اساسی مدیریت داده‌ها 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