افزودن موضوع‌های جدید

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

مدل فرم موضوع

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

در جانگو ساده‌ترین راه برای ساختن یک فرم استفاده ا169 ز فرمِ مدل است که به کمک داده‌ی مدل‌هایی که در فصل ۱۸ام تعریف کرده‌ایم به طور خودکار فرمی را تولید می‌کند. اوّلین فرم خود را در فایل forms.py بسازید که در همان پوشه‌ی حاوی فایل models.py قرار می‌گیرد:

1
2
3
4
5
6
7
8
9
from django import forms

from .models import Topic

class TopicForm(forms.ModelForm):
    class Meta:
        model = Topic
        fields = ['text']
        labels = {'text': ''}

ابتدا ماژول forms و مدل مورد نظر خود، به نام Topic، را وارد می‌کنیم. در ۱ کلاسی به نام TopicForm تعریف می‌کنیم که از forms.ModelForm ارث می‌برد.

ساده‌ترین نسخه‌ی ModelForm از یک کلاس داخلی به نام Meta تشکیل شده است که به جانگو می‌گوید فرم را بر اساس کدام مدل بنا کند و کدام فیلدها را در آن بیاورد. در ۲ فرمی از مدل Topic می‌سازیم و تنها فیلد text را در آن می‌آوریم (۳). کد در ۴ به جانگو می‌گوید که برای فیلد text برچسبی تولید نکند.

آدرس new_topic

URLای که برای یک صفحه‌ی جدید باید کوتاه و گویا باشد. هرگاه کاربر بخواهد موضوع جدیدی اضافه کند باید او را به http://localhost:8000/new_topic/ بفرستیم. الگوی URL برای صفحه‌ی new_topic به صورت زیر است که آن را به learning_logs/urls.py اضافه می‌کنیم:

1
2
3
4
5
6
7
--snip--

urlpatterns = [
    --snip--
    # Page for adding a new topic
    path('new_topic/', views.new_topic, name='new_topic'),
]

این الگو درخواست‌ها را به تابع نمای new_topic() می‌نویسیم که در ادامه آن را می‌نویسیم.

تابع نمای new_topic()

تابع new_topic() باید دو وضعیّت مختلف را مدیریّت کند: یکی درخواست‌های اوّلیّه‌ی صفحه‌ی new_topic (که در آن‌ها باید یک فرم خالی نمایش دهد) و دیگری پردازش هر داده‌ای که در فرم ثبت می‌شود. پس از این که داده‌های ثبت‌شده پردازش شد، باید کاربر به صفحه‌ی topics برگردانده شود:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from django.shortcuts import render, redirect

from .models import Topic
from .forms import TopicForm

--snip--

def new_topic(request):
    """Add a new topic."""
    if request.method != 'POST':
        # No data submitted; create a blank form.
        form = TopicForm()
    else:
        # POST data submitted; process data.
        form = TopicForm(data=request.POST)
        if form.is_valid():
            form.save()
            return redirect('learning_logs:topics')

    # Display a blank or invalid form.
    context = {'form': form}
    return render(request, 'learning_logs/new_topic.html', context)

تابع redirect را وارد می‌کنیم و از آن برای بازگردانی کاربر به صفحه‌ی topics پس از این که موضوع خود را ثبت کرد استفاده می‌کنیم. این تابع نام یک نما را می‌گیرد و کاربر را به آن هدایت می‌کند. علاوه بر این فرمی را که هم‌اکنون نوشتیم، TopicForm، وارد می‌کنیم.

درخواست‌های GET و POST

دو نوع درخواست اصلی که هنگام ساختن برنامه‌های تحت وب استفاده می‌کنیم درخواست‌های GET و POSTاند. درخواست‌های GET را در صفحه‌هایی استفاده می‌کنید که تنها می‌خواهند داده‌هایی را از سرور بگیرند. از درخواست‌های POST زمانی به کار می‌آیند که کاربر بخواهد از طریق یک فرم اطّلاعاتی را ثبت کند. ما در تمام فرم‌های خود از متد POST برای پردازش تمام فرم‌های خود استفاده خواهیم کرد. (چند نوع درخواست دیگر هم وجود دارد امّا از آن‌ها در این پروژه استفاده نمی‌کنیم.)

تابع new_topic() شیء درخواست را به عنوان پارامتر می‌پذیرد. وقتی کاربران در ابتدا این صفحه را درخواست می‌کنند مرورگر آن‌ها یک درخواست GET خواهد فرستاد. زمانی که کاربران فرم را پر کرده و ثبت می‌کنند مرورگر آن‌ها یک درخواست POST ثبت می‌کند. بسته به نوع درخواست ما خواهیم فهمید که کاربران درخواست فرم خالی دارند (یک درخواست GET) یا از ما می‌خواهند که فرم تکمیل‌شده را پردازش کنیم (یک درخواست POST).

آزمونی که در ۱ نوشتیم مشخّص می‌کند که درخواست از نوع GET است یا POST. اگر متد درخواست POST نباشد پس به احتمال زیاد GET خواهد بود پس باید یک فرم خالی برگردانیم (اگر هم نوع درخواست چیز دیگری جز این دو باشد، برگرداندن فرم خالی به لحاظ امنیّتی مشکلی ندارد.) یک نمونه از TopicForm می‌سازیم (۲) و آن را به متغیّر form نسبت می‌دهیم و فرم را در قالب دیکشنری context به قالب می‌فرستیم (۷). چون هنگام نمونه‌سازی TopicForm هیچ نشان‌وندی تعیین نکرده‌ایم جانگو یک فرم خالی می‌سازد که کاربر می‌تواند آن را پر کند.

اگر متد درخواست POST باشد بخش else اجرا می‌شود و داده‌های ثبت‌شده در فرم را پردازش می‌کند. نمونه‌ای از TopicForm می‌سازیم (۳) و داده‌هایی را که کاربر وارد کرده و در request.POST ذخیره شده، به آن می‌دهیم. شیء form که برگردانده می‌شود حاوی اطّلاعاتی است که کاربر ثبت کرده است.

تا زمانی که اطّلاعات ثبت‌شده اعتبارسنجی نشده باشد آن‌ها را در پایگاه داده ذخیره نخواهیم کرد (۴). متد is_valid() بررسی می‌کند که آیا تمام فیلدهای اجباری پر شده‌اند یا نه (تمام فیلدها به صورت پیش‌فرض اجباری‌اند) و آیا داده‌های وارد شده با نوع داده‌ی مورد انتظار انطباق دارند یا نه—برای مثال آیا چنان‌که در models.py در فصل ۱۸ام تعیین کردیم، طول text کمتر از ۲۰۰ حرف است یا نه. اگر همه چیز معتبر باشد save() را فرامی‌خوانیم (۵) که داده‌های دریافتی از فرم را در پایگاه داده می‌نویسد.

به محض ذخیره‌سازی داده‌ها در پایگاه داده می‌توانیم صفحه را ترک کنیم. از redirect() برای بازگرداندن مرورگر کاربر به صفحه‌ی topics استفاده می‌کنیم که کاربر باید بتواند در آن فهرستی موضوعاتی را که همان لحظه وارد کرده ببیند.

متغیّر context در انتهای تابع نما تعریف می‌شود و صفحه به کمک قالب new_topic.html ساخته می‌شود که در ادامه آن را ایجاد می‌کنیم. این کد خارج از تمام عبارت‌های if قرار می‌گیرد؛ زمانی اجرا می‌شود که یک فرم خالی ایجاد شده یا اطّلاعات ثبت‌شده در فرم معتبر نیستند. فرم نامعتبر چند پیام خطای پیش‌فرض نیز دربرمی‌گیرد تا به کاربر در ثبت داده‌های قابل قبول کمک کند.

قالب new_topic

بیایید قالبی جدید به نام new_topic.html بسازیم تا فرم ساخته‌شده را نمایش دهیم.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{% extends "learning_logs/base.html" %}

{% block content %}
  <p>Add a new topic:</p>

  <form action="{% url 'learning_logs:new_topic' %}" method='post'>
    {% csrf_token %}
    {{ form.as_p }}
    <button name="submit">Add topic</button>
  </form>

{% endblock content %}

این قالب base.html را گسترش می‌دهد پس ساختار آن مشابه دیگر صفحه‌های Learning Log خواهد بود. در ۱ یک فرم HTML تعریف می‌کنیم. نشان‌وند action به مرورگر نشانی ارسال اطّلاعات ثبت‌شده در فرم را به مرورگر می‌دهد؛ در این مورد آن را به تابع نمای new_topic() می‌فرستیم. نشان‌وند method به مرورگر می‌گوید که داده‌ها را به صورت یک درخواست POST ثبت کند.

جانگو به کمک تگ قالب {% csrf_token %} مانع دسترسی غیرمجاز نفوذگران به سرور از طریق فرم می‌شود.170 (این نوع حمله را سرقت درخواست بین سایتی می‌نامند.) در ۳ فرم را نمایش می‌دهیم؛ اینجا می‌توانید ببینید که جانگو انجام برخی از کارها مانند نمایش یک فرم را چه‌قدر ساده می‌کند. کافی است متغیّر قالب {{ form.as_p }} را اضافه کنیم تا جانگو تمام فیلدهای مورد نیاز برای نمایش خودکار فرم بسازد. عبارت as_p به جانگو می‌گوید که تمام عنصرهای فرم را به عنوان یک روش ساده و منظّم برای نمایش فرم، به شکل پاراگراف دربیاورد.

جانگو دکمه‌ی ثبت را در فرم‌ها ایجاد نمی‌کند پس در ۴ یکی تعریف می‌کنیم.

ایجاد پیوند به صفحه‌ی new_topic

در ادامه پیوندی را به صفحه‌ی new_topic در صفحه‌ی topics ایجاد می‌کنیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{% extends "learning_logs/base.html" %}

{% block content %}
  <p>Topics</p>
  <ul>
    --snip--
  </ul>

  <a href="{% url 'learning_logs:new_topic' %}">Add a new topic</a>

{% endblock content %}

پیوند را پس از فهرست موضوعات موجود قرار دهید. شکل ۱۹-۱ فرم حاصل را نمایش می‌دهد. از این فرم برای افزودن چند موضوع استفاده کنید.

![][image47]

شکل ۱۹-۱: صفحه‌ی اضافه کردن موضوع جدید