【Python】公式サイトでdjangoを学習する その3

今回もDjangoの公式サイトから学ぼうと思います。

準備


djangoがインストールされているか確認

$ python -m django –version

なければインストール

$ pip install django==2.1

サーバーを起動

$ python manage.py runserver

Viewを書く


views.pyに基本的な機能を果たす関数を書きます。

from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")

def detail(request, question_id):
  return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
  response = "You're looking at the results of question %s."
  return HttpResponse(response % question_id)

def vote(request, question_id):
  return HttpResponse("You're voting on question %s." % question_id)

views.pyとurls.pyを紐づけます。


urls.pyに次のpath()コールを追加すると、新しいviews.pyの関数をpolls.urlsモジュールと結びつけます。

from django.urls import path

from . import views

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

ブラウザを開いて、http://127.0.0.1:8000/polls/100/と打ち込む。

detailsメソッドが実行され、

You’re looking at question 100.と表示される。

実際に動作するviewを書く


各viewには2つの役割があります。

・リクエストされたページのコンテンツを含むHttpResponseオブジェクトを返す事

・Http404のような例外の送出

Django にとって必要なのはHttpResponseか、あるいは例外です。

その2のAPI を使ってみましょう。試しに次のようなindex()ビューを作ります。これは、システム上にある最新の 5 件の質問項目をカンマで区切り、日付順に表示するビューです。

from django.http import HttpResponse

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    output = ', '.join([q.question_text for q in latest_question_list])
    return HttpResponse(output)

 

このコードには問題があります。ビューの中で、ページのデザインがハードコードされています。ページの見栄えを変更するたびに、 Python コードを編集する必要があります。 Django のテンプレートシステムを使って、ビューから使用できるテンプレートを作成し、Python からデザインを分離しましょう。

最初にpolls ディレクトリの中に、 templates ディレクトリを作成します。


$ cd polls

templatesディレクトリを作成

$ mkdir templates

$ cd templates

pollsディレクトリを作成

$ mkdir polls

$ cd polls

index.htmlファイルを作成

$ touch index.html

公式サイトからの引用

最初に、 polls ディレクトリの中に、 templates ディレクトリを作成します。 Django はそこからテンプレートを探します。

プロジェクトの TEMPLATES には、Django がどのようにテンプレートをロードしレンダリングするかが書かれています。デフォルトの設定ファイルでは、 DjangoTemplates バックエンドが設定されており、その APP_DIRS のオプションが True になっています。規約により、 DjangoTemplates は INSTALLED_APPS のそれぞれの “templates” サブディレクトリを検索します。

先ほど作成した templates ディレクトリ内で polls というディレクトリを作成し、さらにその中に index.html というファイルを作成してください。つまり、テンプレートは polls/templates/polls/index.html に書く必要があります。 app_directories テンプレートローダは前述(訳注: APP_DIRS の説明)のように動くため、Django 内でこのテンプレートを単に polls/index.html のように参照できます。

polls/templates/polls/index.htmlを記述

{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}

views.pyを修正


indexビューを更新
from django.http import HttpResponse
from django.template import loader

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
        'latest_question_list': latest_question_list,
    }
    return HttpResponse(template.render(context, request))
ブラウザで “/polls/” を開くと、箇条書きのリストが表示されるはずです(リストには、 チュートリアルその2 で作った “What’s up” という質問が入っていますね)。

render()


renderによって、loaderやHttpResponseをimportする必要がなくなります。

render()関数は、第1引数にrequestオブジェクトを第2引数にテンプレート名を、第3引数(任意)に辞書を受け取ります。

この関数はHttpResponseを返します。

from django.shortcuts import render

from .models import Question

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):
  return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
  response = "You're looking at the results of question %s."
  return HttpResponse(response % question_id)

def vote(request, question_id):
  return HttpResponse("You're voting on question %s." % question_id)

Http404


views.pyを書き換えます。
このビューは、リクエストした ID を持つ質問が存在しないときにHttp404を送出します。

from django.shortcuts import render
from django.http import Http404

from .models import Question

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):

    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})

def results(request, question_id):
  response = "You're looking at the results of question %s."
  return HttpResponse(response % question_id)

def vote(request, question_id):
  return HttpResponse("You're voting on question %s." % question_id)

ショートカット:get_object_or_404


detailビューでショートカットキーを使用します。
from django.shortcuts import get_object_or_404, render
from django.http import Http404

from .models import Question

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 = get_object_or_404(Question, pk=question_id)
    
    return render(request, 'polls/detail.html', {'question': question})

def results(request, question_id):
  response = "You're looking at the results of question %s."
  return HttpResponse(response % question_id)

def vote(request, question_id):
  return HttpResponse("You're voting on question %s." % question_id)

polls/detail.htmlを記述


detail.htmlを作ります。

<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

テンプレート内のハードコードされたURLを削除


このハードコードされた、密結合のアプローチの問題は、プロジェクトにテンプレートが多数ある場合、URLの変更が困難になってしまうことです。しかし、polls.urlsモジュール の path()関数で name 引数を定義したので、テンプレートタグの{%url%}を使用して、URL 設定で定義されている特定の URL パスへの依存をなくすことができますと公式サイトにあります。

{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}

urls.pyも変更

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:question_id>/', views.detail, name='detail'),
    path('<int:question_id>/results/', views.results, name='results'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
    # added the word 'specifics'
path('specifics/<int:question_id>/', views.detail, name='detail'),
]

URL名の名前空間


djangoがurl名を区別する為に書き換えます。

公式サイトから引用

このチュートリアルプロジェクトが持つアプリは polls アプリ1つだけです。実際の Django プロジェクトでは、5個、10個、20個、あるいはそれ以上のアプリがあるかもしれません。 Django はどうやってこれらの間の URL 名を区別するのでしょうか? 例えば、 polls アプリは detail ビューを含みますが、同じプロジェクトにブログのためのアプリがあり、そのアプリも同名のビューを含むかもしれません。 {% url %} テンプレートタグを使ったとき、 Django はどのアプリのビューに対して url を作成すればいいでしょうか? これを Django にどう知らせればいいでしょうか。

答えは、 URLconf に名前空間を追加すること、です。どうぞ polls/urls.py ファイル内で app_name を追加し、アプリケーションの名前空間を設定してください。

from django.urls import path

from . import views

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

index.htmlの書き換え

名前空間つきの詳細ビューを指すように書き換え

{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}

この回は、1度読んだだけだと難しいかもしれませんね。ブラウザでの確認は次のその4を終えたあとで行います。









返信を残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください