فرم ها در جنگو

جنگو

سلام

کاربرد فرم ها که معلوم است اما مشکلی که در نرم افزارهای تحت وب وجود دارد مربوط به پیاده سازی ناصحیح فرم ها و عدم کنترل و پاکسازی ورودی‌ها بوده و ممکن است باعث حملاتی مثل SQLi یا XSS یا نظیر آن شود. اما در صورتی که در جنگو به صورت استاندارد کد زده شود این مشکل وجود نخواهد داشت.

فرمی که قرار است پیاده سازی شود به شکل زیر است:

دو ملاحظه برای پیاده سازی این فرم وجود دارد:

  • اعتبارسنجی کاربران: این فرم قرار است فقط به کاربران مجاز و بعد از ورود به سیستم به ایشان نمایش داده شود. که در این مرحله فرض می کنیم که این کار انجام شده است.
  • در این فرم از دو مدل استفاده شده. یکی مدل تاپیک برای فیلد «Topic» و دیگری مدل پست برای فیلد «Post»

خوب. برای شروع باید یک URL با نام new_topic بسازیم.

myproject/urls.py

تابع ویوی new_topic به شکل زیر می شود:

boards/views.py

در پیاده سازی فعلی، تابع جدید دقیقا مشابه تابع board_topics است.

بر اساس این تابع، باید تمپلیتی را با نام html ایجاد کنیم:

templates/new_topic.html

تا اینجای کار یک بلوک را برای ساخت فرم ایجاد کردیم و همچنین نام board.name به صورت لینکی در آمده تا به صفحۀ مربوط به تاپیک‌ها ارجاع یابد.

آدرس http://127.0.0.1:8000/boards/1/new/ را وارد می کنیم.

توجه شود که هنوز صفحه ای را نساخته ایم که در آن لینکی به این صفحۀ جدید وجود داشته باشد اما اگر به صورت دستی آدرس را وارد کنیم به آن صفحه انتقال خواهیم یافت.

و حالا باید برای آن فایل تست بنویسیم.

boards/tests.py

 

مروری کوتاه بر عناوین تست‌ها!

  • setUp: یک بورد به صورت موقت می سازد تا از آن در طول برنامۀ تست استفاده کند.
  • test_new_topic_view_success_status_code: چک می کند که آیا درست است یا خیر؟
  • test_new_topic_view_not_found_status_code: در صورت خطا کد 404 را باز می گرداند.
  • test_new_topic_url_resolves_new_topic_view: بررسی می کند که ویوی درستی استفاده شده باشد.
  • test_new_topic_view_contains_link_back_to_board_topics_view: چک می کند که لینک برگشت به لیست تاپیک ها درست کار کند.

حالا نوبت به اجرای تست می شود:

این هم از خروجی:

بعد از انجام تست، می خواهیم فرم خود را طراحی کنیم:

templates/new_topic.html

صفحۀ بالا، یک صفحۀ ساده بوده و برای قشنگ کردن آن از Bootstrap 4 استفاده می کنیم.

در برنامۀ بالا اتفاق خاصی نیفتاده و یک فرم با متد POST درون آن تعریف شده است. (فرق بین متد POST و GET مشخص است…)

اما تنها نکتۀ آن استفاده از تگ CSRF (Cross-Site Request Forgery Token) است. از این Token با این هدف استفاده می شود که از ارسال داده‌ها توسط‌ سایت‌های دیگر به سایت ما خودداری شود. وقتی دیتایی به صورت پست به سایت ارسال می شود در ابتدای توکن مربوط به آن را چک می کند و در صورت عدم وجود این توکن، یا معتبر نبودن آن، درخواست را نادیده می گیرد. برای این کار از تگ زیر استفاده می کنیم:

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

مورد بعدی که برای ارسال داده‌ها لازم است مربوط به تنظیم name در قسمت ورودی‌های HTML است. از Name برای کار با داده‌های ارسالی به سمت سرور استفاده می شود.

برای استفاده از دیتای آن هم به شکل زیر عمل می شود:

پیاده سازی ویوی برای دریافت اطلاعات از طریق HTML و شروع یک تاپیک جدید به این شکل است:

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

در خصوص کاربرسنجی هم نیاز به کد زنی طولانی وجود دارد که در بخش دیگر توضیح می دهم اما مشخص کردن کاربری که در سیستم لاگین کرده کار آسانی است که در این جا انجام شده است.

بحش بعدی به این مربوط می شود که با کلیک روی نام بورد، به صفحۀ مربوط برویم و تمام تاپیک‌های مربوط به آن بورد را نشان دهد.

فرم با کلیک روی دکمۀ Post ارسال می شود.

همانطور که در شکل معلوم است با کلیک روی دکمۀ Post داده به درستی ارسال می شود اما بعد از آن چیزی مشاهده نمی شود و لازم است کاری کنیم که بعد از کلیک روی آن دکمه لیستی از تاپیک‌های قبلی به همراه تاپیک جدید نشان داده‌شود. این کار با ویرایشس فایل html صورت می پذیرد.

templates/topics.html

خوب! این هم از این!

دو مفهوم جدید در این قطعه کد (ساخت فرم) مطرح است. در اینجا برای اولین بار از ویژگیِ topics در مدلِ Board استفاده شده. این ویژگی به صورت خودکار توسط جنگو و با استفاده از روابط معکوس (reverse relationship) تولید می شود.

وقتی می گوییم board=board یعنی اینکه در مدل topic فیلد بورد را تنظیم کرده ایم و این کار از طریق ForeignKey(Board) انجام می شود.. با این کار، بوردِ ما متوجه می شود که صاحب یک تاپیک جدید شده و آن را به خود مرتبط می کند.

همینطور به این دلیل از board.topics.all به جای board.topics استفاده کردیم چون board.topics یک Related Manager است و خیلی شبیه Model Manager بوده و مثلاً با استفاده از board.objects قابل دسترسی است. بنابراین برای لیست کردن تمام تاپیک‌های یک بورد باید از متد board.topics.all() استفاده شود. برای فیلتر کردن خروجی هم می توان از متد زیر استفاده کرد.

نکتۀ خیلی مهم دیگر این است که وقتی در حال کدزنی در داخل کدهای پایتون هستیم باید برای استفاده از متدها از پرانتز استفاده کنیم. یعنی topics.all(). اما وقتی در حال کدزنی در محیط تمپلیت‌های هستیم نیازی به پرانتز نیست. یعنی board.topics.all.

مورد بعدی استفاده از ForeignKey برای دسترسی به لیست کاربران است.

همانطور که قبلاً هم اشاره رفت، فیلد starter مربوط به کاربرِ ایجادکنندۀ تاپیک هاست و بنابراین می توان از طریق آن به سایر ویژگی‌ها و فیلدهای یک کاربر دسترسی پیدا کرد. مثل آدرس ایمیل کاربر… برای اینکار از نقطه یا dot استفاده می کنیم؛ مثلِ topic.starter.email

  1. تا به اینجای کار فایل html را ویرایش کردیم و حالا نوبت آن است که دکمۀ new topic را به صفحه اضافه کنیم.

templates/topics.html

  1. و مثل همیشه نوبت نوشتن تست است:

boards/tests.py

تابع test_board_topics_view_contains_link_back_to_homepage  تغییر نام داده و یک assertContains هم برای چک لینکِ مربوط به «new_topic» هم به برنامه اضافه شده است.

یک تست دیگر برای ویوی فرم می نویسیم.

boards/tests.py

فایل test.py رفته‌رفته بزرگ و بزرگ‌تر می شود و باید آن را هم تکه تکه کنیم و به چند فایل تقسیم کنیم اما فعلاً با همین شیوه ادامه می دهیم.

  • setUp: برای ساخت یک کاربر به صورت موقت و استفاده از آن در برنامۀ تست از objects.create_user استفاده می کنیم.
  • test_csrf: مطمئن می شویم که HTML شامل این توکن می شود.
  • test_new_topic_valid_post_data: یک دیتای معتبر ایجاد می کند و مطمئن می شود که ویوی مربوط به تاپیک و پست درست ایجاد شود.
  • test_new_topic_invalid_post_data: یک دیکشنری خالی به برنامه ارسال می کنیم و رفتار برنامه را چک می کنیم.
  • test_new_topic_invalid_post_data_empty_fields: این قسمت مانند بخش قبلی است با این تفاوت که یک سری داده را به سرور ارسال می کنیم اما بخش subject را خالی می فرستیم و واکنش برنامه را می بینیم.

تست می کنیم!

و این هم خروجی:

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

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

 

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

مطلب قبلی: قالب‌هایی با قابلیت استفادۀ مجدد یا Reusable Templates

بدون دیدگاه

پاسخی بگذارید

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

جنگو
ثبت نام یا Sign Up

سلام به عنوان اولین گام باید یک Url برای صفحۀ ثبت نام تعریف کنیم. myproject/urls.py from django.conf.urls import url from django.contrib import admin from accounts import views as accounts_views from boards import views urlpatterns = [ url(r’^$’, views.home, name=’home’), url(r’^signup/$’, accounts_views.signup, name=’signup’), url(r’^boards/(?P<pk>\d+)/$’, views.board_topics, name=’board_topics’), url(r’^boards/(?P<pk>\d+)/new/$’, views.new_topic, name=’new_topic’), url(r’^admin/’, admin.site.urls), …

جنگو
تنظیمات اولیه

سلام برای تمام این کارها لازم است تا برنامۀ جدیدی را ایجاد کنیم. برای انجام این کار وارد فولدری می شویم که فایل py قرار دارد و دستور زیر را وارد می کنیم: django-admin startapp accounts ساختار پروژه‌ بعد از این دستور به شکل زیر می شود. myproject/ |– myproject/ …

جنگو
شمای کلی از اعتبارسنجی برنامه در جنگو

سلام در این فصل قصد داریم به مباحث اعتبارسنجی مثل ثبت نام، ورود به سیستم، خروج از سیستم، بازیابی پسورد و تغییر پسورد بپردازیم. همینطور چگونگی تعیین سطح دسترسی برای کاربران سایت از سایر افراد مطرح می شود. تا به این لحظه یک پروژه به نام Libogram تعریف کردیم که …

هرگونه استفادۀ از این آموزش به صورت رایگان و با ارجاع به تمدن جایز است.