はじめに
- これまで、HTMLとCSSを使って簡単なWebページを構築する方法、またGitとGitHubを使ってコードの変更を追跡し、他の人と共同作業する方法を説明してきました。また、Pythonプログラミング言語にも触れました。
- 今日は、Pythonの
Django
フレームワークを使って動的なアプリケーションを作成する作業に取り掛かります。
Webアプリケーション
これまでのところ、私たちが作成したWebアプリケーションはすべて静的です。つまり、そのWebページを開くたびに、まったく同じに見えます。しかし、私たちが毎日訪れる多くのウェブサイトは、私たちが訪れるたびに変わります。例えば、New York TimesやFacebookのウェブサイトを訪問すると、おそらく今日と明日では違ったものを見ることになるでしょう。このような大規模なサイトでは、変更が行われるたびに従業員が大きなHTMLファイルを手動で編集しなければならないのは無理がありますから、動的なWebサイトは非常に便利です。動的Webサイトは、プログラミング言語(Pythonなど)を利用してHTMLおよびCSSファイルを動的に生成するWebサイトです。この講義では、最初の動的アプリケーションの作成方法について学習します。
HTTP
HTTP (HyperText Transfer Protocol) は、インターネット上でメッセージを送受信するためのプロトコルとして広く受け入れられています。通常、オンライン情報はクライアント (ユーザ) とサーバの間で渡されます。
このプロトコルでは、以下のようにクライアントはサーバにリクエストを送信します。以下の例では、GET
は単なる要求のタイプであり、このコースで説明する3つのうちの1つです。/
は通常、Webサイトのホームページを探していることを示し、3つのドットは、さらに多くの情報を渡すことができることを示します。
要求を受信すると、サーバはHTTP応答を返します。この応答は次のようになります。このような応答には、HTTPバージョン、ステータスコード(200はOKを意味します)、コンテンツの説明、および追加情報が含まれます。
200は多くのステータスコードのひとつで、以下のコードのいくつかは見たことがあるかもしれません。
Django
Django はPythonベースのWebフレームワークで、HTMLとCSSを動的に生成するPythonコードを作成できます。Djangoのようなフレームワークを使用する利点は、多くのコードがすでに作成されており、それを利用できることです。
- まず、Djangoをインストールする必要があります。つまり、pipをまだインストールしていない場合は、pipもインストールする必要があります。
- Pipをインストールしたら、ターミナルで
pip3 install Django
を実行してDjangoをインストールできます。
Djangoをインストールしたら、次のようにして新しいDjangoプロジェクトを作成します。
django-admin startproject PROJECT_NAME
を実行して、プロジェクトのスターターファイルをいくつか作成します。cd PROJECT_NAME
を実行して、新しいプロジェクトのディレクトリに移動します。- 目的のテキストエディタでディレクトリを開きます。いくつかのファイルが作成されています。現時点では、これらのほとんどを確認する必要はありませんが、最初から非常に重要な3つのファイルがあります。
manage.py
は、ターミナルでコマンドを実行するために使用します。編集する必要はありませんが、頻繁に使用します。settings.py
には、新しいプロジェクト用の重要な構成設定がいくつか含まれています。デフォルトの設定もいくつかありますが、そのうちのいくつかは必要に応じて変更します。urls.py
には、特定のURLにアクセスした場合のルーティングに関する指示が含まれています。
-
python manage.py runserver
を実行してプロジェクトを起動します。これにより、開発サーバーが起動します。このサーバーにアクセスするには、提供されているURLにアクセスします。この開発サーバーはローカルマシンで実行されているため、他のユーザはWebサイトにアクセスできません。デフォルトのランディングページが表示されます。 - 次に、アプリケーションを作成します。Djangoプロジェクトは1つ以上のアプリケーションに分割されます。私たちのプロジェクトのほとんどは1つのアプリケーションしか必要としませんが、大規模なサイトはこの機能を利用して1つのサイトを複数のアプリケーションに分割することができます。アプリケーションを作成するには、
python manage.py startapp APP_NAME
を実行します。これにより、views.py
を含む、すぐに役立つディレクトリとファイルが作成されます。 - 次に、新しいアプリケーションをインストールします。これを行うには、
settings.py
に移動し、INSTALLED_APPS
のリストまで下にスクロールして、このリストに新しいアプリケーションの名前を追加します。
ルーティング
アプリケーションを開始するには、次の手順に従います。
- 1. 次に、
views.py
に移動します。このファイルには、いくつかの異なるビューが含まれています。ここでは、ビューをユーザが見たいと思う1つのページと考えることができます。最初のビューを作成するために、request
を取り込む関数を作成します。ここでは、単純にHttpResponse
(応答コード200とWebブラウザに表示可能なテキスト文字列を含む非常に単純な応答) として「Hello, World」を返します。これを行うために、django.http import HttpResponse
を含めました。ファイルは次のようになります。
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def index(request):
return HttpResponse("Hello, world!")
- 2. 次に、作成したビューを特定のURLに関連付ける必要があります。これを行うには、
views.py
と同じディレクトリにurls.py
という別のファイルを作成します。プロジェクト全体のurls.py
ファイルはすでにありますが、アプリケーションごとに個別のファイルを用意することをお勧めします。 - 3. 新しい
urls.py
の中に、ユーザが私たちのウェブサイトを使っている間に訪問するかもしれないURLパターンのリストを作ります。これを行うには、次の手順を実行します。- いくつかのインポートを行わなければなりません:
from django.urls import path
によって、URLをリルートすることができます。from . import viewsはviews.py
で作成した関数をインポートします。 urlpatterns
というリストを作成します。- 必要なURLごとに、URLパスを表す文字列、そのURLにアクセスされたときに呼び出す
views.py
の関数、および (オプションで)path
の名前をname="something"
の形式で指定し、path 関数の呼び出しを含むurlpatterns
リストに項目を追加します。例えば、私たちのシンプルなアプリが今どうなっているかを見てみましょう。
- いくつかのインポートを行わなければなりません:
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index")
]
- 4. ここで、この特定のアプリケーション用に
urls.py
を作成しました。次に、プロジェクト全体用に作成したurls.py
を編集します。このファイルを開くと、admin
というパスがすでに存在することがわかります。このパスについては、後の講義で説明します。新しいアプリに別のパスを追加したいので、urlpatterns
リストに項目を追加します。これは、2番目の引数としてviews.pyから関数を追加するのではなく、urls.pyファイルからのすべてのパスをアプリケーションに含めることを除いて、以前のパスと同じパターンに従います。これを行うには、include("APP_NAME.urls")
と記述します。ここで、include
は、以下のurls.py
に示すように、django.urls
からinclude
を読み込むことでアクセスできる関数です。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('hello/', include("hello.urls"))
]
- 5. これにより、ユーザがこのサイトにアクセスし、検索バーでURLに
/hello
を追加すると、新しいアプリケーション内のパスにリダイレクトされることを指定しました。
python manage.py runserver
を使ってアプリケーションを起動し、提供されたURLにアクセスすると、次のような画面が表示されます。これは、URL localhost:8000/hello
のみを定義しているのに、URL localhost:8000
を定義せず、最後に何も追加していないためです。そこで、検索バーのURLに /hello
を追加すると、
ある程度の成功を収めたので、これまでの経緯を見てみましょう。
localhost:8000/hello/
というURLにアクセスすると、DjangoはベースURL (localhost:8000/
) の後にあるものを調べ、プロジェクトのurls.py
ファイルにアクセスして、helloに一致するパターンを検索しました。- 拡張子を定義したため、この拡張子が見つかったのです。そして、この拡張子を見つけるには、アプリケーション内から
urls.py
ファイルをinclude
する必要があることがわかりました。 - その後、DjangoはURLの一部を無視し(
localhost:8000/hello/
、またはそのすべて)、他のurls.py
ファイルの中で、URLの残りの部分と一致するパターンを探しました。 - これまでの唯一のパス (
""
) がURLの残りの部分と一致していたため、そのパスに関連付けられているviews.py
から関数へとリダイレクトされました。 - 最後に、Djangoは
views.py
内でその関数を実行し、結果(HttpResponse("Hello, world!")
)をWebブラウザーに返しました。
これで、必要に応じて、views.py
内の index
関数を変更して必要なものを返すことができます。変数を追跡し、最終的に何かを返す前に関数内で計算を行うこともできます。
次に、アプリケーションに複数のビューを追加する方法について説明します。BrianとDavidに挨拶するページを作成するために、アプリケーション内の同じステップの多くに従うことができます。
views.py
の内容:
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def index(request):
return HttpResponse("Hello, world!")
def brian(request):
return HttpResponse("Hello, Brian!")
def david(request):
return HttpResponse("Hello, David!")
urls.py
の内容 (アプリケーション内)
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("brian", views.brian, name="brian"),
path("david", views.david, name="david")
]
localhost:8000/hello
にアクセスしてもサイトは変更されませんが、URLに brian
または david
を追加すると別のページが表示されます。
多くのサイトは、URLに含まれるアイテムによってパラメータ化されています。例えば、www.twitter.com/cs50にアクセスするとCS 50のツイートがすべて表示され、www.github.com/cs50にアクセスするとCS 50のGitHubページが表示されます。あなた自身の公開GitHubリポジトリを見つけるには、 www.github.com/YOUR_USERNAME
にアクセスすれば大丈夫です。
これがどのように実装されているかを考えると、GitHubやTwitterのようなサイトがユーザごとに個別のURLパスを持つことは不可能に思えますから、もう少し柔軟なパスを作る方法を調べてみましょう。まず、greet
という一般的な関数を views.py
に追加します。
def greet(request, name):
return HttpResponse(f"Hello, {name}!")
この関数は、要求だけでなく、ユーザ名の追加引数も受け取り、その名前に基づいてカスタムHTTP応答を返します。次に、urls.py
でより柔軟なパスを作成する必要があります。これは次のようになります。
path("<str:name>", views.greet, name="greet")
これは新しい構文ですが、基本的には、URLで特定の単語や名前を検索するのではなく、ユーザが入力する文字列を検索することになります。さて、このサイトを他のいくつかのURLで試してみましょう。
文字列を大文字にするPythonのcapitalize
関数を利用するようにgreet
関数を拡張することで、これらをもう少しきれいに見せることもできます。
def greet(request, name):
return HttpResponse(f"Hello, {name.capitalize()}!")
これは、Pythonにある機能がDjangoでどのように使用されてから返されるのかを示す良い例です。
テンプレート
これまでのところ、HTTPレスポンスはテキストのみでしたが、任意のHTML要素を含めることができます。例えば、index
関数内において、テキストだけではなく、青いヘッダーを返すようにすることもできます。
def index(request):
return HttpResponse("<h1 style=\"color:blue\">Hello, world!</h1>")
views.py
内でHTMLページ全体を作成するのは非常に面倒です。また、プロジェクトの各部分を可能な限り個別のファイルに保存する必要があるため、不適切なデザインにつながります。
そこで今回は、HTMLとCSSを別々のファイルに記述し、それらのファイルをDjangoを使ってレンダリングできるDjangoのテンプレートを紹介します。テンプレートのレンダリングに使用する構文は、次のようになります。
def index(request):
return render(request, "hello/index.html")
次に、そのテンプレートを作成します。これを行うには、アプリ内に templates
というフォルダを作り、その中に hello
(アプリの名前が何であれ)というフォルダを作り、index.html
というファイルを追加します。
次に、新しいファイルに必要なものを追加します。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hello</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
アプリケーションのメインページにアクセスすると、ヘッダーとタイトルが更新されています。
静的なHTMLページを作成するだけでなく、Djangoのテンプレート言語を使用して、アクセスしたURLに基づいてHTMLファイルのコンテンツを変更することもできます。先ほどの greet
関数を変更して試してみましょう。
def greet(request, name):
return render(request, "hello/greet.html", {
"name": name.capitalize()
})
ここでは、3番目の引数を render
関数に渡しています。これはコンテキストと呼ばれます。このコンテキストでは、HTMLファイル内で使用可能にする情報を提供できます。このコンテキストはPython辞書型の形式をとります。次に、greet.html
ファイルを作成します。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hello</title>
</head>
<body>
<h1>Hello, {{ name }}!</h1>
</body>
</html>
2重中括弧という新しい構文が使用されています。この構文を使用すると、context
引数で指定した変数にアクセスできます。試してみましょう。
ここまでは、提供するコンテキストに基づいてHTMLテンプレートを変更する方法について説明しました。しかし、Djangoテンプレート言語はそれよりも強力なので、役に立つ他のいくつかの方法を見てみましょう。
条件式
条件によって、ウェブサイトの表示を変更したい場合があります。たとえばwww.isitchristmas.comというサイトにアクセスすると、次のようなページが表示されます。
このウェブサイトはクリスマスの日に変更され、ウェブサイトはYESと表示されます。私たちでこのようなもの、つまり元旦であるかどうかをチェックするアプリケーションを作成してみましょう。そのために新しいアプリケーションを作ります。
- 1. ターミナルで
python manage.py startapp newyear
を実行します。 - 2.
settings.py
を編集し、INSTALLED_APPS
の1つとして 「newyear」 を追加します。 - 3. プロジェクトの
urls.py
ファイルを編集し、hello
アプリケーション用に作成したものと同様のパスを含めます。
path('newyear/', include("newyear.urls"))
- 4. 新しいアプリケーションのディレクトリ内に別の
urls.py
ファイルを作成し、hello
内のインデックスパスと同様のパスを含むように更新します。
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
]
- 5.
views.py
に index関数を作りましょう。
新しいアプリの準備ができたので、元旦かどうかをチェックする方法を考えてみましょう。これを行うには、Pythonのdatetimeモジュールをインポートします。このモジュールがどのように動作するかを理解するために、ドキュメンテーションを調べ、Pythonインタプリタを使ってDjangoの外部でテストすることができます。
- Pythonインタプリタは、Pythonコードの小さな塊をテストするために使用できるツールです。これを使用するには、ターミナルで
python
を実行すると、ターミナル内でPythonコードを入力して実行できるようになります。インタプリタの使用が終了したら、exit()
を実行して終了します。 - この知識を使用して、今日が新年の日:
now.day == 1 および now.month == 1
の場合にのみTrueと評価されるブール式を作成できます。 - これで、元旦かどうかを評価するための式ができたので、
views.py
のindex関数を更新します。
def index(request):
now = datetime.datetime.now()
return render(request, "newyear/index.html", {
"newyear": now.month == 1 and now.day == 1
})
次に、index.html
テンプレートを作成します。templates
という名前の新しいフォルダ、newyear
という名前のフォルダ、および index.html
という名前のファイルを再度作成する必要があります。このファイルには、次のように記述します。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Is it New Year's?</title>
</head>
<body>
{% if newyear %}
<h1>YES</h1>
{% else %}
<h1>NO</h1>
{% endif %}
</body>
</html>
上記のコードでは、HTMLファイルにロジックを含める場合、論理ステートメントの前後の開始タグと終了タグとして {%
および %}
を使用しています。また、Djangoのフォーマット言語では、if-else
ブロックが終了したことを示す終了タグを含める必要があることにも注意してください。これで、ページを開いて以下を確認できます。
さて、この裏で何が起こっているのかを理解するために、このページの要素を見てみましょう。
実際にWebブラウザーに送信されるHTMLにはNOヘッダーしか含まれていないことに注意してください。つまり、Djangoは作成したHTMLテンプレートを使って新しいHTMLファイルを作成し、それをWebブラウザーに送信します。少しごまかして、条件が常に真であることを確認すると、反対のケースが満たされていることがわかります。
def index(request):
now = datetime.datetime.now()
return render(request, "newyear/index.html", {
"newyear": True
})
スタイリング
CSSファイル (変更されない静的ファイル) を追加する場合は、まず static
というフォルダを作成し、その中に newyear
フォルダを作成してから、styles.css
ファイルを作成します。このファイルでは、最初のレッスンで行ったように、任意のスタイルを追加できます。
h1 {
font-family: sans-serif;
font-size: 90px;
text-align: center;
}
今度は、このスタイル設定をHTMLファイルに組み込むために、HTMLテンプレートの先頭に {% load static %}
という行を追加して、Djangoに static
フォルダ内のファイルにアクセスしてもらいたいということを通知します。次に、以前のようにスタイルシートへのリンクをハードコーディングするのではなく、Django固有の構文を使用します。
<link rel="stylesheet" href="{% static 'newyear/styles.css' %}">
サーバを再起動すると、スタイル設定の変更が実際に行われたことがわかります。
タスク
では、これまでに学んだことを、TODOリストの作成という小さなプロジェクトに適用してみましょう。新しいアプリを作ることから始めましょう。
- 1. ターミナルで
python manage.py startapp
タスクを実行します。 - 2.
settings.py
を編集し、INSTALLED_APPS
の1つとして 「tasks」 を追加します。 - 3. プロジェクトの
urls.py
ファイルを編集し、hello
アプリケーション用に作成したものと同様のパスを含めます。
path('tasks/', include("tasks.urls"))
- 4. 新しいアプリケーションのディレクトリ内に別の
urls.py
ファイルを作成し、hello
内のインデックスパスと同様のパスを含むように更新します。
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
]
- 5.
views.py
にindex関数を作成します。
まず、タスクのリストを作成し、それをページに表示します。views.py
の上部にタスクを保存するPythonリストを作成します。次に、index
関数を更新してテンプレートをレンダリングし、新しく作成したリストをコンテキストとして提供します。
from django.shortcuts import render
tasks = ["foo", "bar", "baz"]
# Create your views here.
def index(request):
return render(request, "tasks/index.html", {
"tasks": tasks
})
次に、テンプレートHTMLファイルを作成します。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Tasks</title>
</head>
<body>
<ul>
{% for task in tasks %}
<li>{{ task }}</li>
{% endfor %}
</ul>
</body>
</html>
ここでは、前述の条件式に似た構文、およびレッスン2のPythonループに似た構文を使用して、タスクをループすることができます。ここでタスクページに移動すると、リストがレンダリングされていることがわかります。
フォーム
現在のすべてのタスクをリストとして表示できるようになったので、新しいタスクを追加できるようにします。そのために、フォームを使ってWebページを更新する方法を見ていきましょう。まず、views.py
に別の関数を追加します。この関数は、新しいタスクを追加するためのフォームを持つページをレンダリングします。
# Add a new task:
def add(request):
return render(request, "tasks/add.html")
次に、urls.py
に別のパスを追加します。
path("add", views.add, name="add")
ここで、add.html
ファイルを作成します。これは index.html
によく似ていますが、本文にリストではなくフォームを含めます。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Tasks</title>
</head>
<body>
<h1>Add Task:</h1>
<form action="">
<input type="text", name="task">
<input type="submit">
</form>
</body>
</html>
しかし、2つの異なるファイルでHTMLの大部分を繰り返しただけなので、今行ったことは必ずしも最良の設計ではありません。Djangoのテンプレート言語は、この貧弱な設計を排除する方法、つまりテンプレート継承を提供してくれます。これにより、ページの一般的な構造を含む layout.html
ファイルを作成できます。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Tasks</title>
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
ここでも {%...%}
を使用して、HTML以外のロジックを表していることに注目してください。この例では、Djangoにこの 「ブロック」 を別のファイルのテキストで埋めるように指示しています。これで、他の2つのHTMLファイルを次のように変更できます。
index.html
:
{% extends "tasks/layout.html" %}
{% block body %}
<h1>Tasks:</h1>
<ul>
{% for task in tasks %}
<li>{{ task }}</li>
{% endfor %}
</ul>
{% endblock %}
add.html
:
{% extends "tasks/layout.html" %}
{% block body %}
<h1>Add Task:</h1>
<form action="">
<input type="text", name="task">
<input type="submit">
</form>
{% endblock %}
レイアウトファイルを拡張することで、繰り返されるコードの大部分を取り除くことができます。これで、インデックスページは変更されず、追加ページも表示されます。
次に、新しいタスクを追加するときにURLに 「/add」 と入力するのは理想的ではないので、ページ間にリンクを追加したいと思うでしょう。リンクをハードコーディングする代わりに、urls.py
の各パスに割り当てた name
変数を使用して、次のようなリンクに作成できます。
<a href="{% url 'add' %}">Add a New Task</a>
「add」 はそのパスの名前です。add.html
でも同様のことができます。
<a href="{% url 'index' %}">View Tasks</a>
しかし、私たちの様々なアプリには index
という名前のいくつかのルートがあるので、これは問題を引き起こす可能性があります。これを解決するには、各アプリの urls.py
ファイルに app_name
変数を追加して、ファイルが次のようになるようにします。
from django.urls import path
from . import views
app_name = "tasks"
urlpatterns = [
path("", views.index, name="index"),
path("add", views.add, name="add")
]
リンクを単純な index
と add
から tasks:index
と tasks:add
に変更します。
<a href="{% url 'tasks:index' %}">View Tasks</a>
<a href="{% url 'tasks:add' %}">Add a New Task</a>
次に、ユーザがフォームを送信したときに、フォームが実際に何かを実行するようにします。これを行うには、add.html
で作成したフォームに action
を追加します。
<form action="{% url 'tasks:add' %}" method="post">
これは、フォームが送信されると、add
URLに戻されることを意味します。ここでは、getメソッドではなくpostメソッドを使用することを指定しました。通常は、フォームによってWebページの状態が変更される可能性があるときに使用します。
Djangoはクロスサイトリクエストフォージェリ (CSRF) 攻撃を防ぐためにトークンを必要とするため、ここでこのフォームにもう少し追加する必要があります。これは、悪意のあるユーザがサイト以外の場所からサーバーに要求を送信しようとする攻撃です。これは、一部のWebサイトにとって非常に大きな問題になる可能性があります。たとえば、銀行のWebサイトに、あるユーザが別のユーザに送金するためのフォームがあるとします。誰かが銀行のウェブサイトの外から送金の命令を送信できたら大変なことになります!
この問題を解決するために、Djangoはテンプレートをレンダリングするレスポンスを送信する際に、サイト上の新しいセッションごとに固有のCSRFトークンも提供します。その後、リクエストが送信されると、Djangoはそのリクエストに関連付けられたCSRFトークンが、最近提供されたトークンと一致することを確認します。したがって、別のサイトの悪意のあるユーザが要求を送信しようとすると、CSRFトークンが無効であるためにブロックされます。このCSRF検証は、Djangoミドルウェアフレームワークに組み込まれています。Djangoミドルウェアフレームワークは、Djangoアプリケーションの要求・応答処理に介入できます。このコースではミドルウェアについて詳しく説明しませんが、必要に応じてドキュメント を参照してください。
この技術をコードに組み込むには、add.html
のフォームに行を追加する必要があります。
<form action="{% url 'tasks:add' %}" method="post">
{% csrf_token %}
<input type="text", name="task">
<input type="submit">
</form>
この行は、Djangoが提供するCSRFトークンを持つ非表示の入力フィールドを追加し、ページをリロードしても何も変更されていないように見えるようにします。ただし、要素を検査すると、新しい入力フィールドが追加されていることがわかります。
Djangoフォーム
これまでのように直接HTMLを記述することでフォームを作成することもできますが、Djangoはユーザから情報を収集するためのさらに簡単な方法を提供しており、それはDjangoフォーム と呼ばれます。このメソッドを使用するには、次のコードをviews.pyの先頭に追加して、forms
モジュールをインポートします。
from django import forms
ここで、NewTaskForm
というPythonクラスを作成して、views.py
内に新しいフォームを作成します。
class NewTaskForm(forms.Form):
task = forms.CharField(label="New Task")
では、このクラスで何が起こっているか見てみましょう。
NewTaskForm
の後のかっこ内に、forms.Form
があることがわかります。これは、新しいフォームが、forms
モジュールに含まれるForm というクラスから継承するためです。Djangoのテンプレート言語やSassを使ったスタイリングで継承がどのように使われるかは既に見てきました。これは、継承がどのように使用されるかの一例であり、より一般的な記述(forms.Form
クラス)を取得し、それを必要なもの(私たちの新しいフォーム)に絞り込むために使用されます。継承はオブジェクト指向プログラミングの重要な部分であり、このコースでは詳しく説明しませんが、このトピックについて学習できるオンラインリソースが多数あります。- このクラスでは、ユーザから収集する情報 (この場合はタスクの名前) を指定できます。
forms.CharField
を記述することによって、これがテキスト入力であることを指定します。Djangoのフォーム・モジュールには、他にも選択可能な入力フィールドが多数含まれています。- この
CharField
内で、ユーザがページをロードしたときに表示されるlabel
を指定します。label
は、フォームフィールドに渡すことができる多くの引数の1つにすぎません。
NewTaskForm
クラスを作成したので、add
ページのレンダリング時にコンテキストに含めることができます。
# Add a new task:
def add(request):
return render(request, "tasks/add.html", {
"form": NewTaskForm()
})
add.html
内で、入力フィールドを先ほど作成したフォームに置き換えることができます。
{% extends "tasks/layout.html" %}
{% block body %}
<h1>Add Task:</h1>
<form action="{% url 'tasks:add' %}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit">
</form>
<a href="{% url 'tasks:index' %}">View Tasks</a>
{% endblock %}
forms
モジュールを使用すると、HTMLフォームを手動で作成するよりもいくつかの利点があります。
- フォームに新しいフィールドを追加する場合は、
views.py
に追加するだけで、追加のHTMLを入力する必要はありません。 - Djangoは、クライアント側の検証、つまりユーザのマシンに対してローカルな検証を自動的に実行します。つまり、フォームが不完全な場合、ユーザはフォームを送信できません。
- Djangoは単純なサーバーサイドの検証、つまりフォームデータがサーバーに到達すると実行される検証を提供します。
- 次のレッスンでは、モデルを使って情報を保存する方法を説明しますが、Djangoを使えば、モデルに基づくフォームを非常に簡単に作成することができます。
これでフォームの設定が完了したので、ユーザが送信ボタンをクリックしたときの動作について説明します。ユーザがリンクをクリックするか、URLを入力して追加ページに移動すると、GET
リクエストがサーバに送信されます。ユーザがフォームを送信すると、サーバに POST
要求が送信されますが、現時点では add
関数では処理されません。POST
メソッドを処理するには、関数に渡される引数、request
に基づいた条件を追加します。次のコードのコメントは、各行の目的を説明しています。
# Add a new task:
def add(request):
# Check if method is POST
if request.method == "POST":
# Take in the data the user submitted and save it as form
form = NewTaskForm(request.POST)
# Check if form data is valid (server-side)
if form.is_valid():
# Isolate the task from the 'cleaned' version of form data
task = form.cleaned_data["task"]
# Add the new task to our list of tasks
tasks.append(task)
# Redirect user to list of tasks
return HttpResponseRedirect(reverse("tasks:index"))
else:
# If the form is invalid, re-render the page with existing information.
return render(request, "tasks/add.html", {
"form": form
})
return render(request, "tasks/add.html", {
"form": NewTaskForm()
})
注意:送信が成功した後にユーザをリダイレクトするには、さらにいくつかインポートする必要があります。
from django.urls import reverse
from django.http import HttpResponseRedirect
セッション
この時点で、増え続けるリストにタスクを追加できるアプリケーションの構築に成功しました。ただし、これらのタスクをグローバル変数として保存すると、ページにアクセスするすべてのユーザがまったく同じリストを参照することになるため、問題が発生する可能性があります。この問題を解決するために、 セッションと呼ばれるツールを使用します。
セッションとは、Webサイトに新しくアクセスするたびに、サーバ側に一意のデータを保存する方法です。
アプリケーションでセッションを使用するには、まずグローバル tasks
変数を削除し、次に index
関数を変更します。最後に、変数 tasks
を使用した他の場所を確認し、request.session["tasks"]
に置き換えます。
def index(request):
# Check if there already exists a "tasks" key in our session
if "tasks" not in request.session:
# If not, create a new list
request.session["tasks"] = []
return render(request, "tasks/index.html", {
"tasks": request.session["tasks"]
})
# Add a new task:
def add(request):
if request.method == "POST":
# Take in the data the user submitted and save it as form
form = NewTaskForm(request.POST)
# Check if form data is valid (server-side)
if form.is_valid():
# Isolate the task from the 'cleaned' version of form data
task = form.cleaned_data["task"]
# Add the new task to our list of tasks
request.session["tasks"] += [task]
# Redirect user to list of tasks
return HttpResponseRedirect(reverse("tasks:index"))
else:
# If the form is invalid, re-render the page with existing information.
return render(request, "tasks/add.html", {
"form": form
})
return render(request, "tasks/add.html", {
"form": NewTaskForm()
})
最後に、Djangoがこのデータを保存できるようにするには、ターミナルで python manage.py migrate
を実行する必要があります。来週は、移行 (マイグレーション) とは何かについて詳しく説明しますが、ここでは、上記のコマンドでセッションを保存できることだけを知っておいてください。
これでこのレッスンは終わりです!次回は、Djangoを使ってデータの保管、アクセス、操作を行うことにします。