Oruji.org
Oruji.orgPersian Tutorials
ورود
ویرایش: 1396/11/22 22:58

آموزش View ها و URLconf ها در جنگو (Django)

در آموزش شروع کار از این کتاب، ما نحوه نصب و راه اندازی سرور جنگو (Django) را توضیح دادیم. در این آموزش شما قواعد ساختن صفحات پویا (داینامیک) از طریق فریم ورک یا چارچوب جنگو (Django) و همچنین استفاده از view ها و URLconf ها را خواهید آموخت.

اولین برنامه جنگو

به عنوان اولین قدم بیایید یک صفحه وب که خروجی معروف Hello world را چاپ می کند را بسازیم.

در صورتیکه شما یک Hello world ساده را بدون استفاده از یک فریم ورک یا چارچوب وب ایجاد کرده باشید، به صورت ساده عبارت Hello world را درون یک فایل متنی وارد کرده و آنرا به عنوان مثال با نام hello.html، در یک پوشه جایی درون وب سرور ذخیره می کردید. دقت کنید که شما دو موضوع کلیدی را در مورد آن صفحه وب در نظر می گیرید: محتویات آن (رشته "Hello world") و URL آن (http://www.example.com/hello.html، و یا شاید http://www.example.com/files/hello.html اگر شما آن فایل را درون یک پوشه زیر مجموعه قرار داده باشید).

در جنگو شما این کار را با کمی تفاوت انجام می دهید. محتویات صفحه توسط یک تابع view تولید می شوند، و URL در یک URLconf تعیین شده است. ابتدا بیایید تابع view حاوی Hello world را بنویسیم.

اولین view شما

داخل پوشه mysite که با دستور django-admin startproject در آموزش قبل ساختید، یک فایل خالی با نام views.py ایجاد کنید. این ماژول پایتون (views.py) حاوی view های ما برای این فصل می باشد. نکته اینکه چیز خاصی برای انتخاب نام views.py وجود ندارد، در جنگو اجباری برای انتخاب نام وجود ندارد، اما ایده خوبی است که مانند یک قرارداد نام آنرا views.py گذاشته تا برای توسعه دهندگان دیگر خوانایی کد شما بیشتر باشد.

view مورد نظر ما بسیار ساده است. اینجا تابع view را مشاهده خواهید کرد، همچنین باید جمله import را داخل فایل views.py تایپ کنید:

from django.http import HttpResponse def hello(request): return HttpResponse("Hello world")

بیایید به بررسی کد بالا بپردازیم:

تابع یک خطی ساده: view فقط یک تابع پایتون می باشد که یک HttpRequest را به عنوان اولین پارامتر دریافت می کند و یک instance از HttpResponse را بر می گرداند. برای اینکه یک تابع پایتون بتواند یک view برای جنگو باشد، باید این دو کار را انجام دهد. (البته استثناهایی نیز وجود دارد اما بعدا به آنها خواهیم پرداخت)

اولین URLconf شما

اگر تا اینجای کار دوباره دستور manage.py runserver را اجرا کنید، باز هم صفحه خوش آمدگویی به جنگو را خواهید دید و اثری از Hello world نخواهید دید. دلیل این است پروژه mysite چیزی درباره view نمی داند و تابع hello را نمی شناسد؛ نیاز است به جنگو گفته شود که یک view در یک URL مشخص قرار دارد. برای این منظور، یعنی اتصال تابع view به یک URL مشخص با جنگو، از یک URLconf استفاده می شود.

یک URLconf شبیه به فهرست مطالب در یک وب سایت است که رابطه بین URL ها و توابع view را نمایش می دهد که در واقع بیان کننده رابطه بین URL و تابعی که آن URL بایستی فراخوانی کند، می باشد.به عبارت دیگر به جنگو می گوید که برای هر URL، باید کدام کد فراخوانی شود. برای مثال هنگامی که شما آدرس http://example.com/foo را درخواست می کنید، تابع foo_view() که در ماژول views.py قرار دارد فراخوانی شود.

هنگامی که شما دستور django-admin.py startproject را در آموزش قبلی اجرا کردید، یک URLconf به صورت اتوماتیک برای شما ساخته شد: فایل urls.py به صورت پیش فرض چیزی شبیه به کد زیر می باشد:

from django.conf.urls.defaults import * # Uncomment the next two lines to enable the admin: # from django.contrib import admin # admin.autodiscover() urlpatterns = patterns('', # Example: # (r'^mysite/', include('mysite.foo.urls')), # Uncomment the admin/doc line below and add 'django.contrib.admindocs' # to INSTALLED_APPS to enable admin documentation: # (r'^admin/doc/', include('django.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: # (r'^admin/', include(admin.site.urls)), )

URLconf به طور پیشفرض شامل قسمت هایی از خصوصیات جنگو می باشد که به صورت کامنت وجود دارد، کد اصلی URLconf به شکل زیر می باشد:

from django.conf.urls.defaults import * urlpatterns = patterns('', )

بیایید کد فوق را مورد بررسی قرار دهیم:

نکته اصلی متغیر urlpatterns می باشد که جنگو درون URLconf به آنرا پیدا خواهد کرد. این متغیر رابطه بین URL ها و کد را تعریف می کند. به صورت پیشفرض همانطور که ملاحظه کردید URLconf خالی است (نکته آنکه، جنگو به چه صورت همانطور که در فصل گذشته مشاهده کردید می داند که صفحه خوش آمد گویی را نشان دهد؟ جواب این است که هنگامی که URLconf شما خالی باشد، جنگو فرض می کند شما تنها یک پروژه را آغازکردید، بنابراین، آن صفحه را نمایش می دهد.)

برای اضافه کردن URL و view به URLconf، تنها کافی است یک تاپل پایتون متصل شده به یک الگوی URL را به تابع view اضافه کنید. در کد زیر نحوه انجام این کار مشخص شده است:

from django.conf.urls.defaults import * from mysite.views import hello urlpatterns = patterns('', ('^hello/$', hello), )

(دقت کنید که ما کدها کامنت را برای کوتاهی کد حذف کردیم، شما می توانید اگر دوست دارید آن ها را در کد خود نگهدارید)

دو تغییر در کد فوق ایجاد شده است:

به طور خلاصه، ما به جنگو گفتیم که هر request ای که به آدرس /hello/ ارسال می شود باید با تابع view یعنی hello کنترل شود.

>>> import sys >>> print sys.path

عموما شما نسبت به تنظیم مسیر پایتون نگرانی نخواهید داشت، پایتون و جنگو به صورت اتوماتیک در پشت صحنه تنظیمات مربوط به مسیر پایتون را انجام می دهند. (تنظیم مسیر پایتون یکی از وظایفی است که manage.py انجام می دهد)

موضوع قابل بحث syntax ای باشد که URLpattern از آن استفاده می کنند، که با حالت آدرس دهی معمولی کمی متفاوت است:

مفهوم فوق با یک مثال خوب قابل توضیح است. اگر بجای الگوی فوق ('^hello/$') از این الگو استفاده کنیم '^hello/' (بدون گذاشتن علامت $ در انتهای URL)، در اینصورت هر URL ای که با /hello/ شروع می شود شامل آدرس URLpattern خواهد شد، مانند /hello/foo و /hello/bar، به همین صورت اگر علامت (^) را از ابتدای URL برداریم ('hello/$') جنگو هر URL ای که با hello/ پایان می یابد را شامل آدرس URLconf خواهد دانست، مانند /foo/bar/hello/، و اگر URL را به این صورت استفاده کنیم hello/، یعنی بدون علامت (^) و ($)، هر URL ای که حاوی hello/ باشد را شامل خواهد شد مانند /foo/hello/bar. بنابراین در اینجا ما از هر دو علامت فوق استفاده کردیم تا تنها آدرس /hello/ بتواند با URL مورد نظر match شود، نه بیشتر و نه کمتر.

نکته دیگر اینکه تابع view یعنی hello به صورت یک شیء بدون فراخوانی تابع به patterns ارسال شده است. این یکی از خصوصیات کلیدی از پایتون (و زبانهای داینامیک دیگر) است: توابع شیء های خاصی هستند، بدین معنی که می توان آن ها را مانند هر متغیر دیگری ارسال کرد.

برای تست کردن تغییرات درون URLconf، سرور جنگو را اجرا کنید، همانطور که در فصل 2 توضیح داده شد، با دستور python manage.py runserver. (اگر سرور از قبل اجرا شده است، دیگر نیازی به اجرا کردن دوباره آن نمی باشد زیرا همانطور که پیش تر گفته شد، به محض ایجاد تغییر در کد و ذخیره آن سرور جنگو خود را بروز رسانی کرده و به صورت اتوماتیک دوباره اجرا می شود.) سپس مرورگر را باز کرده، و به آدرس http://127.0.0.1:8000/hello/ بروید. نوشته Hello world را باید در خروجی تماشا کنید.

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

اشاره ای کوتاه به خطای 404

تا اینجای کار، URLconf ما تنها یک URLpattern تعریف کرده است. چه اتفاقی می افتد اگر یک درخواست با URL متفاوت ارسال شود؟

برای پی بردن به جواب، سرور جنگو را اجرا کرده و یک پیج با همچین آدرسی را باز کنید: http://127.0.0.1:8000/goodbye/ یا http://127.0.0.1:8000/hello/subdirectory، یا حتی http://127.0.0.1:8000/ (روت سایت). شما باید صفحه ای با پیام"page not found" که در شکل 1-3 نشان داده شده است مشاهده کنید. جنگو این پیام را نشان می دهد زیرا درخواست شما URL ای می باشد که در URLconf تعریف نشده است.

پیام خطای 404 جنگو بسیار تواناتر از خطای عادی 404 می باشد. همچنین خطای 404 جنگو دقیقا URLconf استفاده شده و الگوی آن را نشان می دهد. با این اطلاعات نشان داده شده شما می توانید به راحتی دلیل ایجاد شدن خطای 404 را متوجه شوید.

طبیعتا اطلاعات نشان داده شده توسط جنگو برای خطای 404 تنها برای شما ایجاد شده است یعنی برای توسعه دهندگان وب. اگر که نمی خواهید این اطلاعات برای عموم نشان داده نشوند، این نکته اهمیت دارد که جنگو زمانی پیام مخصوص به خود را نشان می دهد که پروژه جنگو در حالت debug باشد. در فصل های آینده نحوه غیر فعال کردن حالت debug را توضیح خواهیم داد. تنها چیزی که باید الان بدانید این است که هر پروژه جنگو هنگامی که ساخته می شود به صورت پیشفرض در حالت debug می باشد و در صورتی که این حالت غیر فعال باشد، جنگو خروجی متفاوتی را نشان خواهد داد.

اشاره ای کوتاه به ریشه سایت (site root)

همانطور که در قسمت قبلی گفته شد، حتی اگر شما به آدرس ریشه سایت http://127.0.0.1:8000/ هم مراجعه می کردید خطای 404 ایجاد می شد. برای اینکه بتوان در URLconf آدرس ریشه سایت را با یک تابع view مرتبط کرد مانند مثال زیر باید عمل کرد:

from mysite.views import hello, my_homepage_view urlpatterns = patterns('', ('^$', my_homepage_view), # ... )

نحوه پردازش درخواست

قبل از ادامه کار برای نوشتن دومین تابع view، اجازه دهید مکثی برای فهمیدن بیشتر نحوه عملکرد جنگو داشته باشیم. هنگامی که شما پیام Hello world را با رفتن به آدرس http://127.0.0.1:8000/hello/ در مرورگر خود مشاهده می کنید، جنگو در پشت صحنه چگونه عمل می کند؟

همه چیز با فایل settings.py آغاز می شود. هنگامی که شما فرمان python manage.py runserver را اجرا می کنید، اسکریپت به دنبال فایل settings.py در مسیر همسان با مسیر manage.py می گردد. این فایل حاوی تمام پیکربندی های مربوط به پروژه جنگو می باشد که همگی به صورت حروف بزرگ انگلیسی می باشند: TEMPLATE_DIRS، DATABSE_NAME، و غیره. مهمترین تنظیم ROOT_URLCONF نام دارد. ROOT_URLCONF به جنگو می گوید که کدام ماژول پایتون باید به عنوان URLconf برای این وب سایت استفاده شود.

هنگامی که django-admin.py startproject فایل های settings.py و urls.py را ایجاد می کرد را بخاطر می آورید؟ فایل به صورت اتوماتیک ساخته شده settings.py حاوی تنظیم ROOT_URLCONF است که به فایل اتوماتیک ساخته شده urls.py اشاره می کند. فایل settings.py را باز کرده و خودتان مشاهده کنید، مانند:

ROOT_URLCONF = 'mysite.urls'

کد فوق برابر با mysite/urls.py می باشد.

زمانی که یک درخواست برای یک URL خاص فرستاده می شود، جنگو URLconf ای که در تنظیم ROOT_URLCONF به آن اشاره شده است را بار گذاری می کند. سپس شروع به چک کردن URL ای که در request آمده است با الگوهای موجود در URLconf، تا زمانی که یک الگوی مرتبط پیدا شود. هنگامی که یک الگوی مرتبط پیدا شد، تابع view مربوط به آن الگو را فراخوانی کرده و یک شیء HttpRequest بعنوان اولین پارامتر به آن ارسال می کند. (در مورد HttpRequest به طور ویژه ای در فصل های بعدی صحبت خواهد شد.)

به طور خلاصه:

  1. یک درخواست به /hello/ فرستاده می شود.
  2. جنگو ریشه URLconf را با نگاه به تنظیم ROOT_URLCONF تعیین می کند.
  3. جنگو به تمامی URLpattern ها برای پیدا کردن یک URL مرتبط با /hello/ جستجو می کند.
  4. اگر URL مرتبطی یافت شد، تابع view مربوط به آن URL فراخوانی می شود.
  5. تابع view یک HttpResponse بر می گرداند.
  6. جنگو HttpResponse را برای نشان دادن نتیجه در یک صفحه وب به شکل مناسب تبدیل می کند.

حالا شما به طور کامل نحوه عمکرد جنگو را می دانید، البته تا این حد که چگونه تابع view به URL مربوط به خود از طریق URLconf وصل می شود.

دومین view: دارای محتوای داینامیک

view قبلی تنها یک نمایش آموزنده از نحوه کار جنگو بود، و مثال یک صفحه پویا (dynamic) بحساب نمی آید، زیرا محتوای صفحه همواره یک چیز ثابت می باشد. در هر زمانی که شما آدرس /hello/ را مشاهده کنید یک چیز ثابت را خواهید دید، که همانند یک فایل استاتیک HTML می باشد.

برای دومین view، صفحه ای داینامیک خواهیم ساخت، یک صفحه وب که زمان و تاریخ فعلی را نمایش خواهد داد. این یک مثال ساده و مناسب می باشد، چرا که در این مثال با دیتابیس یا ورودی کاربر سر و کار نخواهیم داشت، تنها زمان داخلی سرور را به صورت خروجی استفاده خواهیم کرد.

برای نوشتن view فوق باید زمان و تاریخ فعلی را محاسبه کرده، و به صورت یک HttpResponse آنرا بر گردانیم. اگر شما با زبان پایتون آشنایی داشته باشید، باید بدانید که پایتون حاوی یک ماژول به نام datetime می باشد که زمان و تاریخ را محاسبه می کند. در این نحوه استفاده از این ماژول را مشاهده خواهید کرد:

>>> import datetime >>> now = datetime.datetime.now() >>> now datetime.datetime(2008, 12, 13, 14, 9, 39, 2731) >>> print now 2008-12-13 14:09:39.002731

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

برای ساختن یک view جنگو که زمان و تاریخ جاری را نمایش دهد، نیاز به قرار دادن جمله datetime.datetime.now() درون view و برگرداندن یک HttpResponse می باشد. که در کد زیر مشاهده می کنید:

from django.http import HttpResponse import datetime def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)

همچنین تابع hello نیز باید درون views.py وجود داشته باشد، ما تابع hello را در این کد فوق برای اختصار حذف کردیم. در اینجا view کامل را مشاهده می کنید.

from django.http import HttpResponse import datetime def hello(request): return HttpResponse("Hello world") def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)

بررسی کد فوق:

بعد از اضافه کردن تابع جدید به views.py، یک URLpattern نیز داخل urls.py ایجاد می کنیم، که برای جنگو مشخص شود که کدام URL باید view مورد نظر خود را کنترل کند.

from django.conf.urls.defaults import * from mysite.views import hello, current_datetime urlpatterns = patterns('', ('^hello/$', hello), ('^time/$', current_datetime), )

دو تغییر ایجاد شده است. اول اینکه تابع current-datetime را در بالا import کردیم. و دومین تغییر که مهم تر از تغییر اول می باشد، یک URLpattern با آدرس /tim/ برای view جدید اضافه کرده ایم. آیا قلق کار دستتان آمد؟

با نوشتن view جدید و تغییر دادن URLconf سرور را اجرا کرده و درون مرورگر به این آدرس بروید http://127.0.0.1:8000/time/. شما باید زمان جاری را در صفحه مورد نظر مشاهده کنید.

URLconf و مفهوم Loose Coupling

حالا زمان خوبی است برای پرداختن به فلسفه کلیدی پشت URLconfs و جنگو: قانون loose coupling. به عبارت ساده، loose coupling یک شیوه توسعه نرم افزار است که در ساختن بخش های قابل تعویض اهمیت دارد. اگر دو بخش از کد به صورت loosely couple پیاده سازی شده باشند، هنگامی که در یک بخش تغییری ایجاد می کنیم، بر روی بخش دیگر تاثیر نمی گذارد و یا تاثیر بسیار کمی دارد.

URLconf جنگو یک مثال بسیار خوب از این قانون می باشد. در یک برنامه وب جنگو تعریف های URL و توابع view، به صورت loosely couple نامیده می شوند؛ برای مثال، به تابع current_datetime ملاحظه کنید. اگر بخواهیم URL مربوط یه این تابع view را از /time/ به /current-time/ تغییر دهیم، می توانیم به سرعت و بدون هیچ نگرانی نسبت به تابع view مربوط به آن در URLconf تغییر ایجاد کنیم. به همین صورت اگر بخواهیم تابع view را نیز تغییر دهیم می توانیم بدون تاثیر بر روی URL آن این تابع را تغییر دهیم.

بعلاوه اگر بخواهیم تابع current-date را برای چندین URL نمایش دهیم، می توانیم خیلی آسان URLconf را بدون هیچ گونه لمسی در تابع view تغییر دهیم. در مثال که در زیر مشاهده خواهید کرد، current-datetime برای دو URL در دسترس خواهد بود.

urlpatterns = patterns('', ('^hello/$', hello), ('^time/$', current_datetime), ('^another-time-page/$', current_datetime), )

URLconf ها و view ها در عمل به صورت loosely couple پیاده سازی شده اند. ما در سرتاسر این کتاب از طریق مثال ها به این فلسفه مهم (loosely coupling) اشاره خواهیم کرد

سومین view: آدرس های پویا (dynamic)

در تابع current_datetime، محتویات صفحه یعنی زمان و تاریخ نشان داده شده، داینامیک بوده، اما URl (/time/) استاتیک می باشد. در اغلب برنامه های داینامیک وب، یک URL حاوی پارامترهایی است که بر روی خروجی صفحه تاثیر می گذارد. برای مثال یک فروشگاه کتاب آنلاین ممکن است برای هر کتاب URL مخصوص به خود را داشته باشد، مانند /books/243/ و /book/81196/.

بیایید view سوم خودمان را با حالت URL داینامیک ایجاد کنیم که درون URL ساعت را دریافت کند. هدف از ساختن این سایت این است که در صورت وارد کردن /time/plus/1/ خروجی یعنی همان زمان و تاریخ در یک ساعت جلوتر نشان دهد، صفحه /time/plus/2/ زمان را 2 ساعت جلوتر از زمان فعلی نشان دهد، صفحه /time/plus/3/ زمان را 3 ساعت جلوتر نشان دهد و الی آخر.

یک تازه کار یا مبتدی ممکن است فکر کند برای هر ساعت یک تابع view مجزا لازم است:

urlpatterns = patterns('', ('^time/$', current_datetime), ('^time/plus/1/$', one_hour_ahead), ('^time/plus/2/$', two_hours_ahead), ('^time/plus/3/$', three_hours_ahead), ('^time/plus/4/$', four_hours_ahead), )

کد فوق به وضوح مشخص است که ناقص می باشد. نه تنها توابع view اضافه و زائد می باشند، بلکه برنامه اساسا به طور محدود تنها از 4 ساعت تعریف شده پشتیبانی می کند. اگر بخواهیم صفحه ای بسازیم پنج ساعت جلوتر را نشان دهد باید یک تابع View و URLconf برای آن ایجاد کنیم، که کاری تکراری می باشد. نیاز به مقداری اختصار در اینجا داریم.

حالا چطور می توان ساعت مشخص شده در URL را درون برنامه کنترل کرد؟ همانطور که پیش تر ذکر شد، URLpattern یک regular expression می باشد؛ از این رو، ما می توانیم از الگوی \d برای مچ کردن یک یا بیشتر ارقام استفاده کنیم.

urlpatterns = patterns('', # ... (r'^time/plus/\d /$', hours_ahead), # ... )

(ما از # ... برای اشاره کردن به این موضوع که ممکن است URLpattern های دیگری نیز درون مثال وجود داشته باشند استفاده می کنیم.)

این URLpattern جدید با هر آدرسی مانند /time/plus/2/، /time/plus/25/ یا حتی /time/plus/100000000/ مچ می شود. بهتر آن است که عدد وارد شده برای ساعت را محدود کنیم، به طوری که حداکثر عددی که بتوان وارد کرد 99 باشد. این بدین معنی است که می خواهیم تنها به وارد کردن اعداد یک یا دو رقمی اکتفا کنیم، شکل آن در regular expression بدین شکل خواهد بود \d{1,2}:

(r'^time/plus/\d{1,2}/$', hours_ahead),

یکی از جزئیات مهمی که معرفی شده است در اینجا حرف r می باشد که قبل URL استفاده شده است. این حرف به پایتون می گوید که رشته ی بعد از آن یک raw string می باشد، در این نوع رشته علامت (\) ترجمه یا تفسیر نمی شود. در رشته های معمولی پایتون علامت های (\) به حروف ویژه تفسیر می شوند مانند '\n'، که به یک کاراکتر خط جدید تبدیل می شود. هنگامی که از حرف r قبل یک رشته استفاده شود، مفسر پایتون دیگر علامت های (\) را تفسیر نمی کند، بنابراین r'\n' دیگر یک کاراکتر خط جدید نمی باشد و تنها دو کاراکتر می باشد، یک (\) و یک حرف کوچک n. بین کاربرد علامت (\) در پایتون و regular expression یک برخورد طبیعی وجود دارد، بنابراین شدیدا پیشنهاد می شود که هنگامی که می خواهید از regular expression استفاده کنید حتما از raw string ها استفاده کنید. از حالا به بعد، تمام URLpattern ها در این کتاب به صورت raw string خواهند بود.

اکنون که توانستیم توسط یک URL چندین URL مختلف را در URLpattern پشتیبانی کنیم، این مشکل مطرح می شود که چگونه داده مورد نظر در URL را به تابع view انتقال دهیم، به طوری که توسط یک تابع view تمام ساعت ها را کنترل کنیم. این مشکل با قرار دادن پرانتز به دور داده ی مورد نظر در URLpattern قابل حل می باشد. برای مثال فوق، ما می خواهیم شماره ی وارد شده در URL را به تابع view انتقال دهیم، بنابراین عبارت \d{1,2} را درون پرانتز قرار می دهیم:

(r'^time/plus/(\d{1,2})/$', hours_ahead),

آخرین URLconf شامل دو تابع view قبلی می باشد:

from django.conf.urls.defaults import * from mysite.views import hello, current_datetime, hours_ahead urlpatterns = patterns('', (r'^hello/$', hello), (r'^time/$', current_datetime), (r'^time/plus/(\d{1,2})/$', hours_ahead), )

حالا تابع hours_ahead را ایجاد خواهیم کرد که در URLconf با URL مورد نظر یعنی r'^time/plus/(\d{1,2})/$' مرتبط شده است. تابع hours_ahead بسیار شبیه به تابع current_datetime می باشد که در ابتدا نوشته شده است، با یک تفاوت کلیدی: تابع مذکور (hours_ahead) یک آرگومنت اضافه دریافت می کند، که شماره ی ساعت ها می باشد:

from django.http import Http404, HttpResponse import datetime def hours_ahead(request, offset): try: offset = int(offset) except ValueError: raise Http404() dt = datetime.datetime.now() datetime.timedelta(hours=offset) html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt) return HttpResponse(html)

اجازه دهید کد فوق را مورد بررسی قرار دهیم:

با تابع view و URLconf نوشته شده، سرور جنگو را (اگر در حال اجرا نمی باشد) اجرا کنید، و به منظور بررسی اینکه برنامه کار می کند یا خیر به آدرس http://127.0.0.1:8000/time/plus/3/ بروید. سپس به آدرس http://127.0.0.1:8000/time/plus/5/ رفته و همچنین آدرس http://127.0.0.1:8000/time/plus/24/ را نیز امتحان کنید. نهایتا به منظور بررسی اینکه الگوی شما تنها اعداد یک رقمی و دو رقمی را قبول می کند آدرس http://127.0.0.1:8000/time/plus/100/ را نیز امتحان کنید؛ جنگو باید خطای "Page not found" را در این مورد نمایش دهد. همچنین آدرس http://127.0.0.1:8000/time/plus/ (بدون هیچ ساعت تعیین شده ای) نیز باید باعث بروز خطای 404 شود.

صفحات خطای جالب جنگو

چند لحظه ای از برنامه های وبی که تاکنون ساخته ایم لذت ببرید ... حالا بیایید خرابشان کنیم! بیایید به طور عمد یکی از خطاهای پایتون را درون فایل views.py با کامنت کردن عبارت offset = int(offset) در hours_ahead معرفی کنیم.

def hours_ahead(request, offset): # try: # offset = int(offset) # except ValueError: # raise Http404() dt = datetime.datetime.now() datetime.timedelta(hours=offset) html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt) return HttpResponse(html)

سرور جنگو را (اگر در حال اجرا نیست) اجرا کرده و به آدرس /time/plus/3/ بروید. یک صفحه ی خطا با یک مقدار قابل توجهی از اطلاعات را مشاهده خواهید کرد، که همچنین در بالای صفحه پیام TypeError نمایش داده شده است:
"unsupported type for timedelta hours component: Unicode"

چه اتفاقی افتاد؟ تابع datetime.timedelta انتظار دارد پارامتر hours یک integer باشد، و در کد فوق قسمتی از کد که offset را به integer تبدیل می کرد کامنت شده است، که باعث شده است در عبارت datetime.timedelta خطای TypeError ایجاد شود.

هدف از این مثال نشان دادن صفحه ی خطای جنگو بود. مقداری درون صفحه ی خطا بگردید و با اطلاعات مختلف داده شده آشنا شوید.

مسائل قابل توجهی در اینجا وجود دارند:

صفحه ی خطای جنگو برای نشان دادن اطلاعات بیشتر موارد ویژه، از قبیل خطاهای template syntax بسیار توانا می باشد. در زمان بحث در مورد سیستم tamplate جنگو به این موضوع خواهیم پرداخت. برای حالا، خط offset = int(offset) را از حالت کامنت خارج کنید تا تابع view بتواند دوباره کار کند.

آیا شما از آن دسته از برنامه نویسانی هستید که دوست دارید برنامه ی خود را با قرار دادن عبارت print اشکال زدایی کنید؟ شما می توانید برای انجام چنین کاری بدون بکار بردن عبارت print از صفحه ی خطای جنگو استفاده کنید. در هر نقطه ای از view به صورت موقت یک assert False قرار دهید. سپس شما می توانید متغیرهای محلی و وضعیت برنامه را مشاهده کنید:

def hours_ahead(request, offset): try: offset = int(offset) except ValueError: raise Http404() dt = datetime.datetime.now() datetime.timedelta(hours=offset) assert False html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt) return HttpResponse(html)

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