پیاده‌سازی فرم ها به شیوۀ استاندارد

سلام

جنگو برای پیاده سازی و ساخت فرم ها یک ماژول آماده دارد که در forms قرار گرفته است. جنگو با دو نوع فرم با نام های forms.Form و forms.ModelForm کار می کند. کلاس Form یک کلاس عمومی برای ساخت انواع فرم ها بوده و در زمانی استفاده می شود که فرمِ ساخته شده به صورت مستقیم با لایۀ مدل در برنامۀ ما کاری نداشته باشد. به همین ترتیب کلاس ModelForm برای ارتباط مستیم با لایۀ مدل بوده و به عنوان زیرکلاسی از کلاس Form شناخته می شود.

برای شروع یک فایل با نام forms.py درون فولدر boards ایجاد می کنیم.

boards/forms.py

from django import forms
from .models import Topic

class NewTopicForm(forms.ModelForm):
    message = forms.CharField(widget=forms.Textarea(), max_length=4000)

    class Meta:
        model = Topic
        fields = ['subject', 'message']

این فرم، اولین فرم ماست. این مورد یک ModelForm است که با مدل تاپیک مرتبط شده است. فیلدِ subject در لیست fields در کلاس Meta به فیلد subject در کلاس Topic مرتبط می شود. یک فیلد دیگر هم به نام message تعریف شده که مربوط به متن پیامِ پستی است که قصد ذخیرۀ آن را داریم.

یک نکتۀ مهم و در عین حال ساده این است که کلاس Meta به عنوان زیر کلاس NewTopiForm تعریف شده و بنابراین تمام فیلدهای کلاس Meta را به ارث می برد…

خوب حالا فایل views.py را ویرایش می کنیم.

from django.contrib.auth.models import User
from django.shortcuts import render, redirect, get_object_or_404
from .forms import NewTopicForm
from .models import Board, Topic, Post

def new_topic(request, pk):
    board = get_object_or_404(Board, pk=pk)
    user = User.objects.first()  # TODO: get the currently logged in user
    if request.method == 'POST':
        form = NewTopicForm(request.POST)
        if form.is_valid():
            topic = form.save(commit=False)
            topic.board = board
            topic.starter = user
            topic.save()
            post = Post.objects.create(
                message=form.cleaned_data.get('message'),
                topic=topic,
                created_by=user
            )
            return redirect('board_topics', pk=board.pk)  # TODO: redirect to the created topic page
    else:
        form = NewTopicForm()
    return render(request, 'new_topic.html', {'board': board, 'form': form})

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

if request.method == 'POST':
    form = NewTopicForm(request.POST)
    if form.is_valid():
        topic = form.save()
        return redirect('board_topics', pk=board.pk)
else:
    form = NewTopicForm()
return render(request, 'new_topic.html', {'form': form})

در خط اول چک می کنیم که نوع فرم به شکل POST است یا به شکل GET. اگر متد به شکل پست بود یعنی اینکه کاربر یک سری داده را به سمت سرور ارسال کرده است. بنابراین داده‌های آن را درون متغیری به نام form می ریزیم. form = NewTopicForm(request.POST)

گام بعدی این است که چک کنیم آیا ورودی های فرم به درستی تنظیم شده یا خیر. برای اینکار از تابع ()form.is_valid استفاده می کنیم و اگر همه چیز درست بود آن را با استفاده از تابع ()form.save در دیتابیس ذخیره می کنیم. تابع ()save علاوه بر ذخیره سازی در دیتابیس، شی مربوط به فرم را هم باز می گرداند که ما آن را در Topic ذخیره می کنیم.

بعد از ذخیره سازی هم باید به صفحۀ مناسب دیگری ریدایرکت کنیم که این کار هم در خط مربوط به return صورت پذیرفته است. این کار از  ارسال دوبارۀ فرم ها به وسیلۀ دکمۀ F5 هم جلوگیری می کند.

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

return render(request, 'new_topic.html', {'form': form})

این کد به این معناست که برای مشاهدۀ خطاها لازم است صفحۀ new_topic.html را به روز رسانی کنیم.

اگر درخواست به صورت GET بود یک فرم را به صورت خام و خالی مقدار دهی می کنیم و این کار با این تابع انجام می شود:

 form = NewTopicForm()

مثل همیشه نوبت به تست می رسد.

python manage.py test

و خروجی:

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
...............
--------------------------------------------------------
Ran 15 tests in 0.522s

OK
Destroying test database for alias 'default'...

APIهای ساخت فرم در جنگو خیلی بیشتر از اعتبارسنجی داده‌ها کاربرد دارد. این ها حتی برای ما صفحات HTML را می سازند.

برای استفادۀ کامل از این ها باید قالب new_topic.html را بروز کنیم.

templates/new_topic.html

{% extends 'base.html' %}

{% block title %}Start a New Topic{% endblock %}

{% block breadcrumb %}
  <li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li>
  <li class="breadcrumb-item"><a href="{% url 'board_topics' board.pk %}">{{ board.name }}</a></li>
  <li class="breadcrumb-item active">New topic</li>
{% endblock %}

{% block content %}
  <form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit" class="btn btn-success">Post</button>
  </form>
{% endblock %}

تگ form دارای سه حالت در زمان رندر است: form.as_ul،form.as_table و form.as_p برای سه حالت در زمان رندر است. این حالات برای نمایش و رندر سریع تمام فیلدهای یک فرم در صفحه استفاده می شود. همانطور که از نامش معلوم است از as_table  برای نمایش فرم ها در حالت جدول بندی استفاده می شود و از as_ul  برای نمایش فیلدهای فرم به عنوان لیست هایی در HTML و…

در حالت قبلی فرم ما زیباتر نمایش داده می شد. حالا باید آن را بهتر کنیم. شاید در نگاه اول کار سخت تری بیاید اما واقعیت این است که این کار بهترین روش است. مثلا وقتی ۵۰ فیلد داشته باشیم می توانیم تمام آن ها را با تایپ دستور سادۀ {{ form.as_p }} انجام دهیم.

علاوه بر آن با استفاده از Forms API در جنگو هم ورودی‌ها اعتبارسنجی می شوند و هم خطاهای مربوط به هرکدام نمایش داده می شوند. برای مثال سعی کنید یک فرم خالی را ارسال کنید.

نکتۀ مهم: اگر در زمان ارسال فرم خالی خطاهایی نظیر   مشاهده شد باید بدانید که این موضوع به جنگو ارتباطی نداشته و مربوط به خطای مرورگر است. برای غیرفعالسازی آن کافی است عبارت novalidate  را به تگ مربوط به فرم اضافه کنیم. <form method=”post” novalidate> البته اگر هم تغییر ندهیم اتفاق خاصی نمی افتد اما در فرم هایی که تعداد فیلدها زیاد است باید این کار را برای استانداردسازی انجام دهیم.

نکتۀ بسیار مهم بعدی:  هیچ چیزی به اسم client-side validation وجود ندارد! ممکن است بعضی از سایت ها برای این کار (اعتبارسنجی سمت کاربر) از جاوا اسکریپت و … استفاده کنند و بهانۀ آن ها هم کاهش بار سرور یا زیبایی و کاربری سریعتر و آسانتر باشد اما مسئله این است که این کار به راحتی قابل دور زدن است و کاربر ممکن است داده های مخرب را به سرور ارسال کند.

نهایت کاری که می شود انجام داد این است که در وهلۀ اول داده ها را با استفاده از جاوااسکریپت و در سمت کاربر اعتبارسنجی کنیم و در صورت صحت به سرور بفرستیم و یک بار هم در آن جا اعتبارسنجی شود. در این حالت در صورتی که کاربر قصد خرابکاری و تغییر در کدها را نداشته باشد در مرحلۀ دوم (اعتبار سنجی در سرور) داده ها تماماً بدون مشکل پاس می شوند چون یکبار با جاوااسکریپت بررسی شده اند.

از قابلیت های دیگر Form APIها این است که می توانیم برای فیلدهای فرم، راهنما قرار دهیم که به آن Help text می گوییم و قابلیت پیاده سازی هم در کلاس Form و هم در کلاس Model را دارد.

boards/forms.py

from django import forms
from .models import Topic

class NewTopicForm(forms.ModelForm):
    message = forms.CharField(
        widget=forms.Textarea(),
        max_length=4000,
        help_text='The max length of the text is 4000.'
    )

    class Meta:
        model = Topic
        fields = ['subject', 'message']

خروجی:

همچنین می توان فرم را به خصوصیت های دیگر هم مقید کرد:

boards/forms.py

from django import forms
from .models import Topic

class NewTopicForm(forms.ModelForm):
    message = forms.CharField(
        widget=forms.Textarea(
            attrs={'rows': 5, 'placeholder': 'What is on your mind?'}
        ),
        max_length=4000,
        help_text='The max length of the text is 4000.'
    )

    class Meta:
        model = Topic
        fields = ['subject', 'message']

ترجمۀ اختصاصی توسط تمدن

 

مطلب بعدی:زیباسازی فرم ها با Bootstrap

مطلب قبلی:فرم ها در جنگو

0 پاسخ

دیدگاه خود را ثبت کنید

تمایل دارید در گفتگو شرکت کنید؟
نظری بدهید!

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *