pyhaya’s diary

プログラミング、特にPythonについての記事を書きます。Djangoや機械学習などホットな話題をわかりやすく説明していきたいと思います。

Djangoのチュートリアル 5 URLのハードコードを解消する

今回は、HTMLファイル内でリンクを張る際に、URLのハードコードを避けるプラクティスを紹介します。以下は前回までの記事です。

Djangoのチュートリアル 1 インストールから最初のビュー作成まで - pyhaya’s diary
Djangoのチュートリアル 2 モデルを作成する - pyhaya’s diary
Djangoのチュートリアル 2 モデルを作成する - pyhaya’s diary

pyhaya.hatenablog.com

目次

ハードコードとはどのような状態をいうのか

プログラミングにおいてコードが「ハードコード」された状態というのはDjnagoに限らず、嫌われる傾向があります。なぜか、それはコードが変更に弱くなるからです。一部を変更すると、ほかに何か所も同時に変更しなくてはいけない。ソースコードが巨大になるほどこれはミスを誘発しやすくなることは想像に難くありません。

今までのコードのどこがハードコードされているか?

ハードコードがいけないのはわかりました。では今まで書いてきたコードのどこが悪いでしょうか?以下のコードを見てみましょう。

polls/templates/polls/index.html

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

これはdetail.htmlへのリンクを表しています。しかしurls.pyを見てみると、detail.htmlのURLはviews.detailに書かれていることがわかります。同じ場所へのリンクが2か所に分かれてしまっています。

するとこんなことが起きます。views.detailのURLを何か理由があって別のものに変更しなくてはならなくなりました。この時、urls.pyのURLのみならず、HTMLファイルまで踏み込んで変更を反映させなくてはいけません。

これはソースコードが巨大になるほど大きな作業になることが想像できます。変更箇所は少ないほうがミスは少なくなります。

どう変更するか

DjangoにはURLのハードコードを避けるための仕組みがちゃんと備わっています。まず、現時点でのコードの状態を確認しておきます。

polls/templates/polls/index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset='utf-8'>
    </head>
    <body>
        {% if latest_question_list %}
        <ul>
            {% for question in latest_question_list %}
            <li><a href="/polls/{{ question.id }}/">    <!-- 問題の箇所-->
                {{ question.question_text }}
            </a></li>
            {% endfor %}
        {% else %}
        <p>No polls are available</p>
        {% endif %}
    </body>
</html>

polls/views.py

from django.shortcuts import render
from django.http import HttpResponse

from .models import Question

# Create your views here.
def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list' : latest_question_list}
    return render(request, 'polls/index.html', context)

def detail(request, question_id):
    question = Question.objects.get(pk=question_id)
    return render(request, 'polls/detail.html', {'question' : question})

polls/urls.py

from django.urls import path

from . import views

urlpatterns = [
        path('', views.index, name='index'),
        path('<int:question_id>/', views.detail, name='detail'),
        ]

urls.pypathの引数を見てください。name=...という部分があります。これは今まで一切説明はしてきませんでした。これが実はハードコードを解消するために大きな役割を果たします。

ハードコードを解消したものが下になります。

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

最も特徴的なのが{% url %}というタグを使っているところです。そのあとに'detail'という文がありますが、これはurls.pyname属性がdetailであるURLにリンクをつなげるということです。

urls.pyを見てみると、name='detail'となっているのはviews.detailをビューに指定しているURLです。このように、name='detail'としているURLがあったので、question.idhrefに読み込みます。

このようなコードの書き方はURLの逆引きと呼ばれます(反対に、URLからビューを見つけるのを正引きと呼びます。)

これでハードコードは解消されました。このように変更してもページの表示のされ方は変わりません。

まとめ

今回は、{% url %}テンプレートタグというものを用いてURLをハードコードしない方法を学びました。