Django フォーム

HTMLフォームは、Webサイトのインタラクティブ性を実現する古典的な方法です。この章では、Djangoを使用してユーザーが送信したフォームデータを処理する方法を紹介します。


HTTP リクエスト

HTTPプロトコルは「リクエスト-レスポンス」の方式で動作します。クライアントがリクエストを送信する際、リクエストにデータを添付できます。サーバーはリクエストを解析することで、クライアントから送られてきたデータを取得し、URLに基づいて特定のサービスを提供します。

GET メソッド

以前のプロジェクトに search.py ファイルを作成して、ユーザーのリクエストを受け取ります:

/HelloWorld/HelloWorld/search.py ファイルコード:

from django.http import HttpResponse from django.shortcuts import render # フォーム def search_form(request): return render(request, 'search_form.html') # リクエストデータを受け取る def search(request): request.encoding='utf-8' if 'q' in request.GET and request.GET['q']: message = '検索内容:' + request.GET['q'] else: message = '空のフォームが送信されました' return HttpResponse(message)

テンプレートディレクトリ templates に search_form.html フォームを追加します:

/HelloWorld/templates/search_form.html ファイルコード:

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>初心者向けチュートリアル(example.com)</title> </head> <body> <form action="/search/" method="get"> <input type="text" name="q"> <input type="submit" value="検索"> </form> </body> </html>

urls.py のルールを次のように変更します:

/HelloWorld/HelloWorld/urls.py ファイルコード:

from django.conf.urls import url from . import views,testdb,search urlpatterns = [ url(r'^hello/$', views.サンプル), url(r'^testdb/$', testdb.testdb), url(r'^search-form/$', search.search_form), url(r'^search/$', search.search), ]

アクセスアドレスhttp://127.0.0.1:8000/search-form/そして検索すると、結果は次のようになります:

POST メソッド

上記では GET メソッドを使用し、ビューの表示とリクエスト処理を2つの関数に分けて処理しました。

データを送信する際は、POST メソッドがより一般的に使用されます。以下ではこのメソッドを使用し、1つのURLと処理関数で、ビューの表示とリクエスト処理を同時に行います。

templates に post.html を作成します:

/HelloWorld/templates/post.html ファイルコード:

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>初心者向けチュートリアル(example.com)</title> </head> <body> <form action="/search-post/" method="post"> {% csrf_token %} <input type="text" name="q"> <input type="submit" value="検索"> </form> <p>{{ rlt }}</p> </body> </html>

テンプレートの末尾に、フォーム処理結果のためのプレースホルダーとして rlt マーカーを追加します。

フォームの後ろにはもう1つ{% csrf_token %}のタグがあります。csrf の正式名称は Cross Site Request Forgery です。これは Django が提供する、偽装リクエストの送信を防ぐ機能です。POST メソッドで送信されるフォームには、このタグが必須です。

HelloWorld ディレクトリに search2.py ファイルを新規作成し、search_post 関数を使用して POST リクエストを処理します:

/HelloWorld/HelloWorld/search2.py ファイルコード:

# -*- coding: utf-8 -*- from django.shortcuts import render from django.views.decorators import csrf # POSTリクエストデータを受け取る def search_post(request): ctx ={} if request.POST: ctx['rlt'] = request.POST['q'] return render(request, "post.html", ctx)

urls.py のルールを次のように変更します:

/HelloWorld/HelloWorld/urls.py ファイルコード:

from django.conf.urls import url from . import views,testdb,search,search2 urlpatterns = [ url(r'^hello/$', views.hello), url(r'^testdb/$', testdb.testdb), url(r'^search-form/$', search.search_form), url(r'^search/$', search.search), url(r'^search-post/$', search2.search_post), ]

アクセスhttp://127.0.0.1:8000/search-post/表示結果は次のとおりです:

上記のインスタンスを完了すると、ディレクトリ構造は次のようになります:

HelloWorld
|-- HelloWorld
|   |-- __init__.py
|   |-- __init__.pyc
|   |-- search.py
|   |-- search.pyc
|   |-- search2.py
|   |-- search2.pyc
|   |-- settings.py
|   |-- settings.pyc
|   |-- testdb.py
|   |-- testdb.pyc
|   |-- urls.py
|   |-- urls.pyc
|   |-- views.py
|   |-- views.pyc
|   |-- wsgi.py
|   `-- wsgi.pyc
|-- TestModel
|   |-- __init__.py
|   |-- __init__.pyc
|   |-- admin.py
|   |-- admin.pyc
|   |-- apps.py
|   |-- migrations
|   |   |-- 0001_initial.py
|   |   |-- 0001_initial.pyc
|   |   |-- __init__.py
|   |   `-- __init__.pyc
|   |-- models.py
|   |-- models.pyc
|   |-- tests.py
|   `-- views.py
|-- db.sqlite3
|-- manage.py
`-- templates
    |-- base.html
    |-- hello.html
    |-- post.html
    `-- search_form.html

Request オブジェクト

各ビュー関数の最初のパラメータは HttpRequest オブジェクトです。次の サンプル() 関数のように:

from django.http import HttpResponse

def サンプル(request):
    return HttpResponse("Hello world")

HttpRequest オブジェクトには、現在のリクエストURLに関する情報が含まれます:

属性

説明

path

リクエストされたページのフルパスで、ドメイン名は含みません。例: "/hello/"。

method

リクエストで使用されるHTTPメソッドの文字列表現。大文字で表されます。例:

if request.method == 'GET':
    do_something()
elif request.method == 'POST':
    do_something_else()

GET

すべてのHTTP GETパラメータを含む辞書風オブジェクト。QueryDict ドキュメントを参照してください。

POST

すべてのHTTP POSTパラメータを含む辞書風オブジェクト。QueryDict ドキュメントを参照してください。

サーバーが空のPOSTリクエストを受け取る場合も起こり得ます。つまり、フォームがHTTP POSTメソッドでリクエストを送信しても、フォームにはデータがない場合があります。したがって、if request.POST を使用してHTTP POSTメソッドかどうかを判断することはできません。if request.method == "POST" を使用すべきです(この表のmethod属性を参照)。

注意: POST にはファイルアップロード情報は含まれません。FILES属性を参照してください。

REQUEST

便宜上、この属性はPOST属性とGET属性を合わせたものですが、特殊な点があり、最初にPOST属性を探し、次にGET属性を探します。PHPの$_REQUESTを参考にしています。

例えば、GET = {"name": "john"} かつ POST = {"age": '34'} の場合、REQUEST["name"] の値は "john"、REQUEST["age"] の値は "34" です。

GET と POST を使用することを強くお勧めします。これらの2つの属性の方がより明示的で、書かれたコードも理解しやすくなります。

COOKIES

すべてのCookieを含む標準のPython辞書オブジェクト。キーと値は両方とも文字列です。

FILES

すべてのアップロードファイルを含む辞書風オブジェクト。FILES の各キーは <input type="file" name="" /> タグの name 属性の値です。FILES の各値も標準のPython辞書オブジェクトであり、次の3つのキーを含みます:

  • filename: アップロードファイル名。Python文字列で表されます。
  • content-type: アップロードファイルのContent type
  • content: アップロードファイルの元の内容

注意:リクエストメソッドがPOSTであり、リクエストページ内の <form> に enctype="multipart/form-data" 属性がある場合のみ、FILES はデータを持ちます。それ以外の場合、FILES は空の辞書です。

META

利用可能なすべてのHTTPヘッダー情報を含む辞書。例:

  • CONTENT_LENGTH
  • CONTENT_TYPE
  • QUERY_STRING: 解析されていない生のクエリ文字列
  • REMOTE_ADDR: クライアントのIPアドレス
  • REMOTE_HOST: クライアントのホスト名
  • SERVER_NAME: サーバーのホスト名
  • SERVER_PORT: サーバーのポート

META では、これらのヘッダーに接頭辞HTTP_を付けたものが Key となり、コロン(:)の後ろが Value となります。例:

  • HTTP_ACCEPT_ENCODING
  • HTTP_ACCEPT_LANGUAGE
  • HTTP_HOST: クライアントが送信したHTTPホストヘッダー情報
  • HTTP_REFERER: 参照元ページ
  • HTTP_USER_AGENT: クライアントのuser-agent文字列
  • HTTP_X_BENDER: X-Benderヘッダー情報

user

django.contrib.auth.models.User オブジェクトであり、現在ログインしているユーザーを表します。

アクセスユーザーが現在ログインしていない場合、user は django.contrib.auth.models.AnonymousUser のインスタンスとして初期化されます。

user の is_authenticated() メソッドを使用して、ユーザーがログインしているかどうかを判断できます:

if request.user.is_authenticated():
    # Do something for logged-in users.
else:
    # Do something for anonymous users.

Django の AuthenticationMiddleware がアクティブな場合にのみ、この属性は使用可能です。

session

唯一読み書き可能な属性で、現在のセッションを表す辞書オブジェクトです。Django のセッションサポートがアクティブな場合にのみ使用可能です。

raw_post_data

未解析の生のHTTP POSTデータ。高度な処理の際に役立ちます。

Request オブジェクトには便利なメソッドもいくつかあります:

メソッド説明
__getitem__(key) GET/POST のキーに対応する値を返します。先にPOSTを取得し、次にGETを取得します。キーが存在しない場合は KeyError を送出します。
これにより、辞書構文を使用して HttpRequest オブジェクトにアクセスできます。
例えば、request["foo"] は、最初に request.POST["foo"] を実行し、次に request.GET["foo"] を実行する操作と同じです。
has_key() request.GET または request.POST にパラメータで指定されたKeyが含まれているかどうかを確認します。
get_full_path() クエリ文字列を含むリクエストパスを返します。例: "/music/bands/the_beatles/?print=true"
is_secure() リクエストが安全な場合、Trueを返します。つまり、HTTPSリクエストが送信された場合です。

QueryDict オブジェクト

HttpRequestオブジェクトでは、GETおよびPOST属性はdjango.http.QueryDictクラスのインスタンスです。

QueryDictは辞書に似たカスタムクラスで、1つのキーが複数の値に対応する場合を処理するために使用されます。

QueryDictは標準の辞書メソッドをすべて実装しています。さらに、いくつかの特有のメソッドも含まれます:

メソッド 説明

__getitem__

標準の辞書の処理とは少し異なり、Keyが複数のValueに対応する場合、__getitem__()は最後のvalueを返します。

__setitem__

パラメータで指定されたkeyのvalueリスト(Pythonのリスト)を設定します。注意:これはmutable(変更可能な)QueryDictオブジェクトでのみ呼び出すことができます(つまり、copy()によって生成されたQueryDictオブジェクトのコピーです)。

get()

keyが複数のvalueに対応する場合、get()は最後のvalueを返します。

update()

パラメータはQueryDictでも標準の辞書でもかまいません。標準の辞書のupdateメソッドとは異なり、このメソッドは辞書のitemsを置き換えるのではなく追加します:

>>> q = QueryDict('a=1')

>>> q = q.copy() # to make it mutable

>>> q.update({'a': '2'})

>>> q.getlist('a')

 ['1', '2']

>>> q['a'] # returns the last

['2']

items()

標準の辞書のitems()メソッドとは少し異なり、このメソッドは単一値ロジックの__getitem__()を使用します:

>>> q = QueryDict('a=1&a=2&a=3')

>>> q.items()

[('a', '3')]

values()

標準の辞書のvalues()メソッドとは少し異なり、このメソッドは単一値ロジックの__getitem__()を使用します:

さらに、QueryDictには次の表に示すようないくつかのメソッドもあります:

メソッド 説明

copy()

オブジェクトのコピーを返します。内部実装はPython標準ライブラリのcopy.deepcopy()を使用します。このコピーはmutable(変更可能)です。つまり、このコピーの値を変更できます。

getlist(key)

パラメータのkeyに対応するすべての値をPythonリストとして返します。keyが存在しない場合は空のリストを返します。何らかのリストを返すことが保証されています。

setlist(key,list_)

keyの値をlist_に設定します(__setitem__()とは異なります)。

appendlist(key,item)

keyに関連付けられた内部リストにitemを追加します。

setlistdefault(key,list)

setdefaultとは少し異なり、単一のvalueではなくリストをパラメータとして受け取ります。

lists()

items()とは少し異なり、keyのすべての値をリストとして返します。例:

>>> q = QueryDict('a=1&a=2&a=3')

>>> q.lists()

[('a', ['1', '2', '3'])]

urlencode()

クエリ文字列形式でフォーマットされた文字列を返します(例:"a=2&b=3&b=5")。

その他の拡張