مبانی کار با فرم‌ها در جنگو

این مقاله به معرفی مفهوم فرم‌ها در وب، نحوه کار آن‌ها در HTML، تفاوت روش‌های GET و POST، نقش جنگو در مدیریت فرم‌ها و ساختار کلاس Form می‌پردازد. همچنین توضیح می‌دهد که چگونه جنگو فرآیند ساخت، پردازش و اعتبارسنجی فرم‌ها را ساده و ایمن می‌کند.

فرمجنگوGET و POST

~13 دقیقه مطالعه • بروزرسانی ۲۳ اسفند ۱۴۰۴

مقدمه

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


فرم‌ها در HTML

در HTML، یک فرم مجموعه‌ای از عناصر داخل تگ <form> است که به کاربر اجازه می‌دهد داده‌هایی مانند متن، گزینه‌ها یا فایل‌ها را وارد کرده و به سرور ارسال کند. برخی از این عناصر مانند inputهای متنی یا checkboxها ساده هستند، اما برخی دیگر مانند date picker یا slider نیازمند JavaScript و CSS هستند.


ویژگی‌های ضروری فرم

هر فرم باید دو ویژگی مهم داشته باشد:

  • action: آدرس مقصدی که داده‌ها باید به آن ارسال شوند.
  • method: روشی که داده‌ها با آن ارسال می‌شوند.

برای مثال، فرم ورود به جنگو ادمین شامل چند input از نوع‌های مختلف است و داده‌ها را با روش POST به مسیر /admin/ ارسال می‌کند.


روش‌های GET و POST

در فرم‌ها تنها دو روش GET و POST استفاده می‌شوند. روش POST داده‌ها را بسته‌بندی کرده و به سرور ارسال می‌کند، در حالی که GET داده‌ها را به صورت رشته‌ای در URL قرار می‌دهد.


کاربردهای GET

  • مناسب برای درخواست‌هایی که وضعیت سیستم را تغییر نمی‌دهند.
  • مناسب برای جستجو یا درخواست‌هایی که قابل bookmark شدن هستند.

کاربردهای POST

  • مناسب برای درخواست‌هایی که داده‌ها را تغییر می‌دهند.
  • مناسب برای فرم‌های حساس مانند password.
  • امن‌تر در برابر حملات، به‌ویژه همراه با CSRF protection.

نقش جنگو در مدیریت فرم‌ها

مدیریت فرم‌ها کاری پیچیده است: آماده‌سازی داده‌ها، ساخت HTML، اعتبارسنجی ورودی‌ها و ذخیره‌سازی آن‌ها. جنگو این فرآیند را ساده و ایمن می‌کند و سه بخش اصلی را مدیریت می‌کند:

  • آماده‌سازی و ساختاردهی داده‌ها برای نمایش.
  • ایجاد فرم‌های HTML.
  • دریافت و پردازش داده‌های ارسال‌شده.

اگرچه می‌توان همه این کارها را به صورت دستی انجام داد، اما جنگو ابزارهایی دارد که این فرآیند را بسیار ساده‌تر می‌کند.


فرم‌ها در جنگو

در یک برنامه وب، واژه form می‌تواند به موارد مختلفی اشاره کند: فرم HTML، کلاس Form در جنگو، داده‌های ارسال‌شده یا مجموعه کامل این اجزا. در جنگو، قلب این سیستم کلاس Form است.


کلاس Form در جنگو

کلاس Form مشابه مدل‌ها عمل می‌کند. همان‌طور که مدل‌ها ساختار داده‌ها را تعریف می‌کنند، فرم‌ها نیز ساختار ورودی‌ها و نحوه نمایش آن‌ها را مشخص می‌کنند. هر field در فرم به یک input در HTML تبدیل می‌شود.
برای مثال، ModelForm فیلدهای مدل را به فیلدهای فرم تبدیل می‌کند و این همان چیزی است که جنگو ادمین بر اساس آن ساخته شده است.


ویجت‌ها

هر فیلد فرم با یک widget نمایش داده می‌شود. ویجت‌ها عناصر رابط کاربری هستند و می‌توان آن‌ها را تغییر یا سفارشی‌سازی کرد.


ایجاد، پردازش و رندر فرم‌ها

رندر کردن فرم در قالب مشابه رندر کردن هر شیء دیگر است، اما تفاوت‌هایی دارد. برخلاف مدل‌ها که معمولاً بدون داده کاربردی نیستند، فرم‌ها حتی بدون داده نیز قابل استفاده‌اند، زیرا هدف آن‌ها دریافت ورودی از کاربر است.


نحوه استفاده از فرم در ویو

در ویو معمولاً فرم را ایجاد می‌کنیم، نه اینکه آن را از پایگاه داده دریافت کنیم. فرم می‌تواند:

  • خالی باشد.
  • با داده‌های یک مدل پر شده باشد.
  • با داده‌های ارسال‌شده توسط کاربر مقداردهی شود.

مثال ساده از ساخت فرم


from django import forms

class ContactForm(forms.Form):
    name = forms.CharField()
    email = forms.EmailField()

جمع‌بندی

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

مقدمه

ساخت یک فرم یکی از مهم‌ترین بخش‌های تعامل کاربر با وب‌سایت است. چه بخواهیم تنها نام کاربر را دریافت کنیم و چه بخواهیم یک فرم پیچیده با ده‌ها فیلد بسازیم، جنگو ابزارهای قدرتمندی برای ساخت، نمایش و پردازش فرم‌ها در اختیار ما قرار می‌دهد. این مقاله روند ساخت یک فرم ساده تا استفاده از Form و ModelForm را توضیح می‌دهد.


ساخت یک فرم ساده HTML

فرض کنید می‌خواهیم نام کاربر را دریافت کنیم. در قالب HTML چنین فرمی خواهیم داشت:


<form action="/your-name/" method="post">
    <label for="your_name">Your name: </label>
    <input id="your_name" type="text" name="your_name" value="{{ current_name }}">
    <input type="submit" value="OK">
</form>

این فرم داده‌ها را با روش POST به مسیر /your-name/ ارسال می‌کند. اگر متغیر current_name در کانتکست قالب وجود داشته باشد، مقدار آن در فیلد ورودی نمایش داده می‌شود.


پردازش داده‌های فرم

پس از ارسال فرم، درخواست POST شامل داده‌های فرم به سرور ارسال می‌شود. شما باید یک view داشته باشید که این داده‌ها را دریافت کرده و پردازش کند. در فرم‌های واقعی ممکن است ده‌ها فیلد وجود داشته باشد و نیاز به اعتبارسنجی، پیش‌پر کردن داده‌ها یا چندین بار ارسال و ویرایش باشد.


در چنین شرایطی استفاده از امکانات جنگو بسیار ساده‌تر و کارآمدتر است.


ساخت فرم در جنگو

کلاس Form

برای ساخت فرم در جنگو، ابتدا یک کلاس Form تعریف می‌کنیم:


from django import forms

class NameForm(forms.Form):
    your_name = forms.CharField(label="Your name", max_length=100)

این کلاس یک فیلد your_name دارد. ویژگی label متن نمایشی فیلد را مشخص می‌کند و max_length هم محدودیت طول ورودی را تعیین می‌کند. این مقدار هم در HTML و هم در اعتبارسنجی سمت سرور اعمال می‌شود.


هر فرم دارای متد is_valid() است که:

  • در صورت معتبر بودن داده‌ها مقدار True برمی‌گرداند.
  • داده‌های معتبر را در cleaned_data قرار می‌دهد.

خروجی اولیه فرم به صورت زیر خواهد بود:


<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" maxlength="100" required>

توجه کنید که تگ <form> و دکمه ارسال در این خروجی وجود ندارد و باید در قالب اضافه شوند.


ساخت ویو برای پردازش فرم

برای پردازش فرم، معمولاً همان ویویی که فرم را نمایش می‌دهد، داده‌های ارسال‌شده را نیز پردازش می‌کند:


from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import NameForm

def get_name(request):
    if request.method == "POST":
        form = NameForm(request.POST)
        if form.is_valid():
            return HttpResponseRedirect("/thanks/")
    else:
        form = NameForm()

    return render(request, "name.html", {"form": form})

اگر درخواست GET باشد، یک فرم خالی ساخته می‌شود. اگر درخواست POST باشد، داده‌ها به فرم «بایند» می‌شوند. اگر فرم معتبر نباشد، دوباره با داده‌های قبلی نمایش داده می‌شود تا کاربر آن را اصلاح کند.


قالب HTML

قالبی که فرم را نمایش می‌دهد بسیار ساده است:


<form action="/your-name/" method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Submit">
</form>

جنگو با استفاده از {{ form }} تمام فیلدها را به HTML تبدیل می‌کند.


محافظت CSRF

جنگو به صورت پیش‌فرض از Cross Site Request Forgery جلوگیری می‌کند. هنگام ارسال فرم با روش POST باید از تگ {% csrf_token %} استفاده کنید.


ورودی‌های HTML5 و اعتبارسنجی مرورگر

اگر فرم شامل URLField، EmailField یا فیلدهای عددی باشد، جنگو از ورودی‌های HTML5 مانند url، email و number استفاده می‌کند. مرورگر ممکن است اعتبارسنجی سخت‌گیرانه‌تری اعمال کند. برای غیرفعال کردن آن می‌توانید از ویژگی novalidate استفاده کنید یا ویجت دیگری تعیین کنید.


اطلاعات بیشتر درباره کلاس‌های فرم

تمام فرم‌ها زیرکلاس‌های django.forms.Form یا django.forms.ModelForm هستند. اگر فرم مستقیماً برای افزودن یا ویرایش یک مدل استفاده می‌شود، ModelForm می‌تواند زمان و کدنویسی زیادی را کاهش دهد، زیرا فیلدها را بر اساس مدل می‌سازد.


جمع‌بندی

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

مقدمه

در سیستم فرم‌های جنگو، درک تفاوت میان فرم بایند و فرم آن‌بایند اهمیت زیادی دارد. این تفاوت تعیین می‌کند که فرم داده دارد یا نه، آیا می‌توان آن را اعتبارسنجی کرد و چگونه باید آن را در قالب نمایش داد. علاوه بر این، جنگو ابزارهای قدرتمندی برای مدیریت فیلدها، ویجت‌ها، داده‌های فرم و رندر سفارشی ارائه می‌دهد.


فرم‌های بایند و آن‌بایند

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


برای تشخیص وضعیت فرم، از ویژگی is_bound استفاده می‌شود.


کار با فیلدهای فرم

یک فرم کاربردی‌تر مانند فرم تماس می‌تواند شامل چندین فیلد باشد:


from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField(widget=forms.Textarea)
    sender = forms.EmailField()
    cc_myself = forms.BooleanField(required=False)

در این مثال از CharField، EmailField و BooleanField استفاده شده است. هر فیلد نوع داده و رفتار خاص خود را دارد.


ویجت‌ها

هر فیلد دارای یک widget است که تعیین می‌کند در HTML چگونه نمایش داده شود. برای مثال، CharField به صورت پیش‌فرض از TextInput استفاده می‌کند. اگر نیاز به textarea باشد، باید ویجت را تغییر داد، همان‌طور که در فیلد message انجام شده است.


داده‌های فرم

پس از فراخوانی is_valid() و معتبر بودن داده‌ها، داده‌های پردازش‌شده در cleaned_data قرار می‌گیرند. این داده‌ها به انواع پایتونی تبدیل شده‌اند. برای مثال، cc_myself یک مقدار بولین خواهد بود.


نمونه‌ای از پردازش داده‌ها در ویو:


from django.core.mail import send_mail

if form.is_valid():
    subject = form.cleaned_data["subject"]
    message = form.cleaned_data["message"]
    sender = form.cleaned_data["sender"]
    cc_myself = form.cleaned_data["cc_myself"]

    recipients = ["[email protected]"]
    if cc_myself:
        recipients.append(sender)

    send_mail(subject, message, sender, recipients)
    return HttpResponseRedirect("/thanks/")

کار با قالب فرم

برای نمایش فرم کافی است آن را در کانتکست قالب قرار دهید. استفاده از {{ form }} باعث رندر خودکار فیلدها می‌شود.


نکته مهم

خروجی فرم شامل تگ <form> و دکمه ارسال نیست. این موارد باید در قالب اضافه شوند.


قالب‌های قابل استفاده مجدد برای فرم‌ها

خروجی HTML فرم از طریق یک قالب داخلی تولید می‌شود. می‌توان با تعریف یک قالب سفارشی و تنظیم FORM_RENDERER این خروجی را تغییر داد.


نمونه قالب سفارشی:


{% for field in form %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
    </div>
{% endfor %}

قالب‌های گروه فیلد

هر فیلد دارای متد as_field_group() است که مجموعه عناصر مرتبط با فیلد را نمایش می‌دهد: برچسب، ویجت، خطاها و متن راهنما.


نمونه استفاده:


{{ form.non_field_errors }}
<div class="fieldWrapper">
  {{ form.subject.as_field_group }}
</div>
<div class="fieldWrapper">
  {{ form.message.as_field_group }}
</div>

رندر دستی فیلدها

برای کنترل کامل، می‌توان فیلدها را به صورت دستی رندر کرد:


{{ form.non_field_errors }}
<div class="fieldWrapper">
    {{ form.subject.errors }}
    <label for="{{ form.subject.id_for_label }}">Email subject:</label>
    {{ form.subject }}
</div>

نمایش خطاهای فرم

خطاهای هر فیلد با {{ form.field_name.errors }} نمایش داده می‌شوند. این خطاها به صورت یک لیست HTML با کلاس errorlist رندر می‌شوند.


نمونه:


<ul class="errorlist">
    <li>Sender is required.</li>
</ul>

جمع‌بندی

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

مقدمه

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


حلقه‌زدن روی فیلدهای فرم

اگر برای هر فیلد از HTML مشابهی استفاده می‌کنید، می‌توانید با یک حلقه {% for %} کدهای تکراری را حذف کنید:


{% for field in form %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
        {% if field.help_text %}
          <p class="help" id="{{ field.auto_id }}_helptext">
            {{ field.help_text|safe }}
          </p>
        {% endif %}
    </div>
{% endfor %}

ویژگی‌های مهم در هر فیلد

هر field در حلقه یک BoundField است و ویژگی‌های مفیدی دارد:

  • field.errors: نمایش خطاهای اعتبارسنجی به صورت <ul class="errorlist">.
  • field.field: دسترسی به شیء اصلی Field و ویژگی‌هایی مانند max_length.
  • field.help_text: متن راهنمای مرتبط با فیلد.
  • field.html_name: نام HTML فیلد، همراه با پیشوند فرم.
  • field.id_for_label: شناسه HTML فیلد برای استفاده در <label>.
  • field.is_hidden: تشخیص اینکه فیلد مخفی است یا نه.
  • field.label: متن برچسب فیلد.
  • field.label_tag: برچسب HTML کامل فیلد.
  • field.legend_tag: مشابه label_tag اما با <legend> برای فیلدهای چندبخشی.
  • field.use_fieldset: مشخص می‌کند آیا فیلد باید داخل <fieldset> قرار گیرد.
  • field.value: مقدار فعلی فیلد.

نمونه استفاده از fieldset

اگر ویجت فیلد شامل چند ورودی باشد، می‌توان آن را داخل fieldset قرار داد:


{% if field.use_fieldset %}
  <fieldset>
  {% if field.label %}{{ field.legend_tag }}{% endif %}
{% else %}
  {% if field.label %}{{ field.label_tag }}{% endif %}
{% endif %}
{{ field }}
{% if field.use_fieldset %}</fieldset>{% endif %}

حلقه‌زدن روی فیلدهای مخفی و قابل‌مشاهده

گاهی لازم است فیلدهای مخفی را جدا از فیلدهای قابل‌مشاهده مدیریت کنیم. جنگو دو متد برای این کار ارائه می‌دهد:

  • hidden_fields()
  • visible_fields()

نمونه:


{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}

{% for field in form.visible_fields %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
    </div>
{% endfor %}

رندر دستی فیلدها

برای کنترل کامل، می‌توان فیلدها را به صورت دستی رندر کرد:


{{ form.non_field_errors }}
<div class="fieldWrapper">
    {{ form.subject.errors }}
    <label for="{{ form.subject.id_for_label }}">Email subject:</label>
    {{ form.subject }}
</div>

نمایش خطاهای فرم

خطاهای هر فیلد با {{ field.errors }} نمایش داده می‌شوند و به صورت لیست HTML رندر می‌شوند:


<ul class="errorlist">
    <li>Sender is required.</li>
</ul>

برای سفارشی‌سازی بیشتر:


{% if form.subject.errors %}
    <ol>
    {% for error in form.subject.errors %}
        <li><strong>{{ error|escape }}</strong></li>
    {% endfor %}
    </ol>
{% endif %}

جمع‌بندی

حلقه‌زدن روی فیلدهای فرم، مدیریت فیلدهای مخفی و قابل‌مشاهده، استفاده از ویژگی‌های BoundField و سفارشی‌سازی کامل رندر فرم‌ها از مهم‌ترین قابلیت‌های جنگو برای ساخت فرم‌های حرفه‌ای هستند. با این ابزارها می‌توان فرم‌هایی انعطاف‌پذیر، قابل‌توسعه و کاملاً سفارشی ایجاد کرد.

نوشته و پژوهش شده توسط دکتر شاهین صیامی