استفاده از 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ای برابر با 200 را باز می گرداند.
    • test_board_topics_view_not_found_status_code: اگر وجود نداشت کد 404 یا همان 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 باشد، کاربران صرفاً صفحۀ مربوط به خطای 500 Internal Server Error را می بینند.

برای تمرین می خواهیم صفحۀ 404 را از نو برنامه نویسی کنیم:

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های پیشرفته 

بدون دیدگاه

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

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

جنگو
پیکربندی گواهینامۀ https

سلام حالا می خواهیم برنامۀ خودمان را با گواهینامۀ https امن کنیم. ساده ترین کار برای انجام آن Let’s Encrypt است. قبل از Let’s Encrypt هرگز تنظیمات https به این راحتی نبوده و مهمتر اینکه اینکار کاملاً رایگان است. آن ها راه حلی به نام certbot را ارائه داده اند …

جنگو
پیکربندی سرویس ایمیل

سلام یکی از بهتر سرویس دهنده های ایمیل Mailgun با قابلیت ۱۲ هزار ایمیل رایگان در ماه است. به صورت رایگان ثبت نام کنید. برای اینکار باید آن را با سرویس دهندۀ دامین خود تنظیم کنید که در این آموزش از tamadon.org استفاده شده است. حالا اولین رکورد DNS را …

جنگو
پیکربندی NGINX

سلام کار بعدی که قرار است انجام شود؛ تنظیمان Nginx به شکلی است که پاسخگویی و سرویس دهی مربوط به فایل های استاتیک را خود انجام دهد و سایر درخواست ها را به Gunicorn بفرستد. یک فایل پیکربندی با نام boards را درون /etc/nginx/sites-available/ و با محتوای زیر اضافه می …

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