pyhaya’s diary

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

Djangoのチュートリアル 3 ビューとモデルを組み合わせる

Djangoチュートリアルの第三弾です。前回までの記事はこちらです。
pyhaya.hatenablog.com
pyhaya.hatenablog.com
今回行うことは、前回作成したモデルと前々回作成したビューを組み合わせて、実際のウェブページにモデルを組み込むことです。

サイトURLとビューを結びつける

最初に、最も簡単なものとしてサイトのURLをビューに反映させてみます。例えば、/polls/4を訪れたらここは4番のページだよといった感じで表示されるようにしたいと考えます。

urls.pyを編集する

このような機能を実現するためには、まずURLの情報をビューに渡す必要があります。そのような機能はurls.pyに書くことができます。

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'),    # <- この行を追加
        ]

'/'という文が増えたことがわかります。この一文の役割は、int型の変数question_idviews.indexに引数として渡すことです。例えばURL /polls/4にアクセスすると、question_id=3となりviews.detailに渡されます。

views.pyを編集する

では、views.pyindex関数がquestion_idを引数として受け取れるように変更しましょう。

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

# Create your views here.
def detail(request, question_id):    # <- question_idを引数リストに入れた
    return HttpResponse("Hello, World. This is polls application number {}"
            .format(question_id))

これで、python manage.py runserverとしてサーバーを起動してhttp://127.0.0.1:8000/polls/4/のようなページにアクセスすると、「Hello, World. This is polls application number 4」のように表示されるはずです。

モデルとビューを結びつける

データベースからデータをとってくる

次に、データベースからデータをとってきてそれを表示してみます。そのためには、ビューをどうにかしてデータベースにつなげる必要があります。データベースには、models.pyに書かれているモデルが登録されていることを考えると、views.pymodels.pyを結び付ければよいことがわかります。

polls/views.py

from django.shortcuts import render

from .models import Question

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

さて、先ほど書いたindex関数は跡形もありません。一行ずつ見てみましょう。

Question.objects.order_by('-pub_date')[:5]

Questionというのはmodels.py中のQuestionクラスのことであることはよいと思います。そのあとのobjects以下でデータベースの中からQuestionのオブジェクトとして登録されているものをとってきます。order_byでは、並び替えて表示しろという指示です。何について並び替えるのかを指定しているのが引数です。Questionクラスにpub_dateというフィールドを作ったのを覚えているでしょうか?忘れた人は第二回の記事を参照してください。order_byの引数に'-pub_date'をとることによってpub_dateが新しいほうから古いほうへ並び替えられます('pub_date'なら逆順になります)。[:5]を指定することによって最初から5個だけ取り出します。

データベースからデータを取り出す方法には様々あります。ほかのものにも興味がある人は、以下のサイトが詳しいです。
qiita.com

context = ...

contextは辞書型のオブジェクトです。今回の場合には、キーとしてlatest_question_listが、そして対応する値には、上で取得したデータが格納されています。コンテクストのキーは、HTMLファイルを編集するときに使います。キーの名前を

{{ キーの名前 }}

のようにして書くことによって値がHTMLに埋め込まれます。このコンテクストはreturnの行で、レンダリングされてindex.htmlに反映されます。

HTMLを作る

HTMLファイルの置き場

今までは、views.pyのなかでページの見た目を決めてきました。しかしこれには限界があります。やはりWebページを作るのはHTMLのほうが得意ですし実績もあるので、Djangoでもページの見た目は最終的にHTMLに投げます。

しかし、Djangoで使うHTMLファイルは、一般的なHTMLファイルとは少し違います。Pythonの文が埋め込まれています。さて、新しいファイルを作るときにいつも問題になるのが、ファイルをどこに置くか問題です。一般的には、各アプリケーションごとにtemplatesディレクトリを作って、その中にアプリケーションの名前が付いたフォルダを作って、その中に入れます。なので今回は、次のような階層構造になるはずです。

pollsディレクトリ内部

.
├── admin.py
├── apps.py
├── __init__.py
├── migrations
│   ├── 0001_initial.py
│   ├── __init__.py
│   └── __pycache__
│       ├── 0001_initial.cpython-36.pyc
│       └── __init__.cpython-36.pyc
├── models.py
├── __pycache__
│   ├── admin.cpython-36.pyc
│   ├── apps.cpython-36.pyc
│   ├── __init__.cpython-36.pyc
│   ├── models.cpython-36.pyc
│   ├── urls.cpython-36.pyc
│   ├── views.cpython-35.pyc
│   └── views.cpython-36.pyc
├── templates         # <- ここ
│   └── polls
│       └── index.html
├── tests.py
├── urls.py
└── views.py

HTMLを書く

では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>

見た目は、ふつうのHTMLみたいな感じですが、変な行が何行かあります。7行目とか、ふつうのHTMLでは見られません。これはPythonが理解できるコードです。確かに、普段Pythonで使いそうな感じの構文です。しかし、上で書いたようにこれはあくまでPythonです。13行目のendforとかはPythonでは使いませんし{{ }}{% %}Pythonでは使いません。これは、HTMLの文法で書かれた範囲とPythonの部分を明確に分けるための表示です。

if文やfor文を使うときには{% %}を使い、変数を使いたいときには{{ }}を使います。Pythonの文が使われているところを見ると、latest_question_listの文字が見えます。これは先ほどviews.pyでコンテクストのキーとして登録したものです。このようにしてPythonの変数をHTML上で使います。

サーバーを走らせてhttp://127.0.0.1:8000/polls/1/を見てみると、前回登録した「What's up?」が表示されていると思います。これだけだと寂しいので、pub_dataを変えて、様々なQuestionを登録してみると面白いかもしれません。

まとめ

今回書いたことは以下の3点です。

  • URLからviews.pyに引数を渡してページの見た目を操作した
  • データベースからデータをとってきてビューに反映した
  • HTML内にPython風コードを入れて、Pythonでページの見た目を変えられるようにした

pyhaya.hatenablog.com