استفاده از APIها در URL
سلام
حالا می خواهیم صفحۀ مربوط به تاپیکهای یک بورد را کدنویسی کنیم. اولین کار این است که فایل urls.py را ویرایش کنیم:
Libogram/urls.py
from django.conf.urls import url from django.contrib import admin from boards import views urlpatterns = [ url(r'^$', views.home, name='home'), url(r'^boards/(?P<pk>\d+)/$', views.board_topics, name='board_topics'), url(r'^admin/', admin.site.urls), ]
و حالا نوبت نوشتن تابع view است.
boards/views.py
from django.shortcuts import render from .models import Board def home(request): # code suppressed for brevity def board_topics(request, pk): board = Board.objects.get(pk=pk) return render(request, 'topics.html', {'board': board})
در فولدر templates یک فایل جدید با نام topics.html می سازیم:
templates/topics.html
{% load static %}<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{{ board.name }}</title> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"> </head> <body> <div class="container"> <ol class="breadcrumb my-4"> <li class="breadcrumb-item">Boards</li> <li class="breadcrumb-item active">{{ board.name }}</li> </ol> </div> </body> </html>
صفحۀ مربوطه با آدرس http://127.0.0.1:8000/boards/1 را در مرورگر چک می کنیم:
حالا نوبت نوشت فایل tests.py است.
boards/tests.py
from django.core.urlresolvers import reverse from django.urls import resolve from django.test import TestCase from .views import home, board_topics from .models import Board class HomeTests(TestCase): # ... class BoardTopicsTests(TestCase): def setUp(self): Board.objects.create(name='Django', description='Django board.') def test_board_topics_view_success_status_code(self): url = reverse('board_topics', kwargs={'pk': 1}) response = self.client.get(url) self.assertEquals(response.status_code, 200) def test_board_topics_view_not_found_status_code(self): url = reverse('board_topics', kwargs={'pk': 99}) response = self.client.get(url) self.assertEquals(response.status_code, 404) def test_board_topics_url_resolves_board_topics_view(self): view = resolve('/boards/1/') self.assertEquals(view.func, board_topics)
در این تست، از یک متد به نام setUp استفاده شده. در این متد ما یک شی از نوع Board را به صورت موقتی ایجاد می کنیم و تست خود را با آن انجام می دهیم. جنگو در زمان تست، تغییری در دیتابیس اصلی ایجاد نمی کند و ما ناچار به این هستیم که یک دیتابیس در هوا ایجاد کنیم و تمام تغییرات مهاجرتی، اجرای تستها و… را به وسیلۀ آن انجام داده و در آخر هم آن را از بین ببریم. در متد setUp محیطی برای تست تعریف شده است.
-
- test_board_topics_view_success_status_code: برای بوردهای موجود کد حالت یا همان Status Codeای برابر با ۲۰۰ را باز می گرداند.
- test_board_topics_view_not_found_status_code: اگر وجود نداشت کد ۴۰۴ یا همان Page Not Found را باز می گرداند.
- test_board_topics_url_resolves_board_topics_view: بیانگر این است که جنگو از تابع صحیحی جهت view برای نمایش تاپیکها استفاده می کند.
حالا می خواهیم تست خود را اجرایی کنیم:
python manage.py test
اما خروجی همراه با خطا خواهد بود:
Creating test database for alias 'default'... System check identified no issues (0 silenced). .E... ======================================================== ERROR: test_board_topics_view_not_found_status_code (boards.tests.BoardTopicsTests) -------------------------------------------------------- Traceback (most recent call last): # ... boards.models.DoesNotExist: Board matching query does not exist. -------------------------------------------------------- Ran 5 tests in 0.093s FAILED (errors=1) Destroying test database for alias 'default'...
اگر صفحۀ مرورگر را هم باز کنیم خطای boards.models.DoesNotExist: Board matching query does not exist مشاهده می شود.
اگر هم که در تنظیمات سرور مقدار DEBUG=False باشد، کاربران صرفاً صفحۀ مربوط به خطای ۵۰۰ Internal Server Error را می بینند.
برای تمرین می خواهیم صفحۀ ۴۰۴ را از نو برنامه نویسی کنیم:
boards/views.py
from django.shortcuts import render from django.http import Http404 from .models import Board def home(request): # code suppressed for brevity def board_topics(request, pk): try: board = Board.objects.get(pk=pk) except Board.DoesNotExist: raise Http404 return render(request, 'topics.html', {'board': board})
دوباره دستور تست را اجرا می کنیم:
python manage.py test
خروجی
Creating test database for alias 'default'... System check identified no issues (0 silenced). ..... -------------------------------------------------------- Ran 5 tests in 0.042s OK Destroying test database for alias 'default'...
بله دیگه! همانطوری که انتظار داشتیم اجرا می شود:
حالا می خواهیم ویوی board_topics را از نو بنویسیم:
from django.shortcuts import render, get_object_or_404 from .models import Board def home(request): # code suppressed for brevity def board_topics(request, pk): board = get_object_or_404(Board, pk=pk) return render(request, 'topics.html', {'board': board})
دوباره تست می کنیم:
python manage.py test
خروجی:
Creating test database for alias 'default'... System check identified no issues (0 silenced). ..... -------------------------------------------------------- Ran 5 tests in 0.052s OK Destroying test database for alias 'default'...
اقدام بعدی این است که باید نام بوردها را به صورت لینک درست کنیم تا کاربر با کلیک روی آن به صفحهای انتقال یابد که تاپیکهای مربوط به آن بورد در آن نمایش داده میشود.
برای شروع کار، برای کلاس HomeTests باید متدهای تست را پیاده سازی کنیم:
boards/tests.py
class HomeTests(TestCase): def setUp(self): self.board = Board.objects.create(name='Django', description='Django board.') url = reverse('home') self.response = self.client.get(url) def test_home_view_status_code(self): self.assertEquals(self.response.status_code, 200) def test_home_url_resolves_home_view(self): view = resolve('/') self.assertEquals(view.func, home) def test_home_view_contains_link_to_topics_page(self): board_topics_url = reverse('board_topics', kwargs={'pk': self.board.pk}) self.assertContains(self.response, 'href="{0}"'.format(board_topics_url))
در این کلاس هم تابع setUp پیاده سازی شده و علت آن هم این است که باید یک شی موقت از Board ایجاد کنیم و url و response را به setUp انتقال دهیم و با این کار از ریسپانس یکسانی دوباره استفاده کنیم.
تست جدید همان test_home_view_contains_link_to_topics_page است. در متد assertContains چک می شود که آیا response body شامل محتوایی هست یا نه. محتوایی که در فایل تست استفاده می شود همان href از تگ a است. پس به زبان ساده چک می کنیم که آیا response body شامل “/href=”/boards/1 می شود یا خیر.
تست می کنیم:
python manage.py test
خروجی:
Creating test database for alias 'default'... System check identified no issues (0 silenced). ....F. ======================================================== FAIL: test_home_view_contains_link_to_topics_page (boards.tests.HomeTests) -------------------------------------------------------- # ... AssertionError: False is not true : Couldn't find 'href="/boards/1/"' in response -------------------------------------------------------- Ran 6 tests in 0.034s FAILED (failures=1) Destroying test database for alias 'default'...
حالا کدی می نویسیم که مشکل این تست برطرف شود.
templates/home.html
<!-- code suppressed for brevity --> <tbody> {% for board in boards %} <tr> <td> <a href="{% url 'board_topics' board.pk %}">{{ board.name }}</a> <small class="text-muted d-block">{{ board.description }}</small> </td> <td class="align-middle">0</td> <td class="align-middle">0</td> <td></td> </tr> {% endfor %} </tbody> <!-- code suppressed for brevity -->
در واقع صرفا {{ board.name }} به <a href=”{% url ‘board_topics’ board.pk %}”>{{ board.name }}</a> تغییر یافته است.
یکی از تگهای تمپلیت در جنگو {% url %} است که از طریق آن آدرس اپلیکیشن ارسال می شود. پارامتر اول نامِ url است که در urls.py تعریف شده است. (نام و شناسۀ یکتا برای هر آدرس) و بعد از آن هم می توان هر تعداد آرگومانی که نیاز است را ارسال نمود.
اگر هم که آدرس از نوع ساده و ابتدایی باشد نیاز به دنباله نداشته و برای مثال به {% url ‘home’ %} اکتفاء می کنیم.
تست می کنیم:
python manage.py test
خروجی:
Creating test database for alias 'default'... System check identified no issues (0 silenced). ...... -------------------------------------------------------- Ran 6 tests in 0.037s OK Destroying test database for alias 'default'...
و این هم خروجی برنامه در مرورگر:
حالا که Django به شکل یک لینک در آمد باید تست مربوط به آن را هم بنویسیم:
boards/tests.py
class BoardTopicsTests(TestCase): # code suppressed for brevity... def test_board_topics_view_contains_link_back_to_homepage(self): board_topics_url = reverse('board_topics', kwargs={'pk': 1}) response = self.client.get(board_topics_url) homepage_url = reverse('home') self.assertContains(response, 'href="{0}"'.format(homepage_url))
تست می کنیم:
python manage.py test
خروجی:
Creating test database for alias 'default'... System check identified no issues (0 silenced). .F..... ========================================================FAIL: test_board_topics_view_contains_link_back_to_homepage (boards.tests.BoardTopicsTests) -------------------------------------------------------- Traceback (most recent call last): # ... AssertionError: False is not true : Couldn't find 'href="/"' in response -------------------------------------------------------- Ran 7 tests in 0.054s FAILED (failures=1) Destroying test database for alias 'default'..
خطا می دهد چون فایل تمپلیت را آپدیت نکرده ایم.
templates/topics.html
{% load static %}<!DOCTYPE html> <html> <head><!-- code suppressed for brevity --></head> <body> <div class="container"> <ol class="breadcrumb my-4"> <li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li> <li class="breadcrumb-item active">{{ board.name }}</li> </ol> </div> </body> </html>
تست می کنیم:
python manage.py test
خروجی:
Creating test database for alias 'default'... System check identified no issues (0 silenced). ....... -------------------------------------------------------- Ran 7 tests in 0.061s OK Destroying test database for alias 'default'...
و این هم خروجی در مرورگر:
اگر دو سه بار این بخش خوانده شود مشخص می شود که هیچ کار سخت و پیچیده ای انجام نشده و صرفاً نوشتن فایلهای تست کمی ظرافتکاری دارد وگرنه که یک صفحه در تمپلیت ساختیم و آن را به صورت لینکی قرار دادیم و متناظر آن را هم در فایل view و url . همین!
ترجمۀ اختصاصی توسط تمدن
مطلب بعدی: لیستی از الگوهای پرکاربرد URL
مطلب قبلی: URLهای پیشرفته
دیدگاه خود را ثبت کنید
تمایل دارید در گفتگو شرکت کنید؟نظری بدهید!