مقدمه
در این بخش، به بررسی سایر عملیات اساسی مدیریت دادهها 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 است. جنگو دو نوع فرم ارائه میدهد
-
Form
— برای ساخت فرمهای سفارشی که لزوما به یک مدل متصل نیستند. بهعنوان مثال، فرمهای تماس یا جستجو که دادههایشان مستقیما در پایگاه داده ذخیره نمیشوند. -
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