Djangoでよく見る「request」の話
デジタル
Django
改訂履歴
2021/9/3 投稿
1. HttpRequestとは
関数ベースビューを作成する際、関数の第一引数に「request」を入れますよね。
この「request」という引数には「HttpRequestオブジェクト」というものが渡されます。
HttpRequestというのは、私達がブラウザでWEBページにアクセスする際にWEBサーバーへ渡されるリクエストのことです。
このリクエストには下記のような情報が入っています。
リクエスト情報の例
・リクエストメソッド(GETやPOSTなど)
・どのページへアクセスするか
・どんな手段でアクセスしているか(ユーザーエージェント)
・クエリストリング
などなど
サーバーはこのリクエストを受け取ると、「HttpResponse」という形で私達にWEBページを見せてくれます。
Djangoではこの「HttpRequestオブジェクト」をビューの引数に渡します。
views.py
def IndexView(request): # requestはHttpRequestオブジェクト
# do something
# return HttpResponse
どのビューに渡すかはurls.pyで定義します。
urls.pyの中でリクエストのパス(URL)によって呼び出すビューを切り替えています。
urls.py
urlpatterns = [この役割を「URLディスパッチャ」と呼び、URLによってビューを切り替える作業を「ルーティング」と言います。
path('URL部分', views.IndexView, name='index'),
]
■ディスパッチャ(dispatcher)
派遣する人、通信指令係
■ルーティング(routing)
経路の決定
ビューの中ではレンダリングに使用するhtmlテンプレートを選択したり、モデルを読み込んだりと色々なことができます。
また、引数としてビューに渡したHttpRequestを活用することも可能です。
「request」を活用する例としては、
リクエストメソッド(GETやPOST)の種類に応じて処理を変えるといった場面が挙げられます。
views.py
# ビューの中
if request.method == "POST": # もしリクエストメソッドがPOSTなら
# do something
HttpRequestオブジェクトには様々な情報が入っています。
上記の例で出た「.method」には「どんなリクエストメソッドか」という情報が入っています。
他にもメタ情報(request.META)というものには下記のデータが辞書形式で入っています。
request.METAの中身
CONTENT_LENGTH -- リクエスト本文の (文字列としての) 長さです。
CONTENT_TYPE -- リクエスト本文の MIME タイプです。
HTTP_ACCEPT -- レスポンスに対して受け入れ可能なコンテンツのタイプです。
HTTP_ACCEPT_ENCODING -- レスポンスに対して受け入れ可能なエンコーディングです。
HTTP_ACCEPT_LANGUAGE -- レスポンスに対して受け入れ可能な言語です。
HTTP_HOST -- クライアントによって送信された HTTP Host ヘッダです。
HTTP_REFERER -- (存在する場合) リファラページです。
HTTP_USER_AGENT -- クライアントのユーザエージェント文字列です。
QUERY_STRING -- クエリ文字列で、単一の (未解析の) 文字列です。
REMOTE_ADDR -- クライアントの IP アドレスです。
REMOTE_HOST -- クライアントのホスト名です。
REMOTE_USER -- (存在する場合) Web サーバによって認証されたユーザです。
REQUEST_METHOD -- "GET" や "POST" といったです。
SERVER_NAME -- サーバのホスト名です。
SERVER_PORT -- (文字列としての) サーバのポートです。
request.METAはPythonの辞書型ですので、各項目の中身を下記のように取り出し可能です。
request.META["HTTP_USER_AGENT"]"KEY"を指定して"VALUE"を得ます。
>> Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36
ちなみに「request」はcontextという形で指定しなくてもテンプレート変数として利用可能です。
htmlテンプレートで下記のように書けばメタ情報をWEBページ上に表示させることが可能です。
template.html
{% for key, value in request.META.items %}
<p>{{key}}:{{value}}</p>
{% endfor %}
requestの正体が分かってきたところで、よく使う「データのやりとり」について次項で解説します。
2. リクエストとデータのやりとり
データのやりとりと言っても、ここではクライアント → サーバーの一方通行のデータ送信について解説します。
リクエストにデータをのせてサーバーへ送る方法はリクエストメソッドによって異なります。
2.1 - GETリクエストの場合
GETリクエストでサーバーにデータを送る場合は「クエリストリング」を使います。
こちらの記事で少し触れました。
Query String Parameter
クエリストリングとはURLの末尾に付与される"KEY"と"VALUE"の関係を持つ文字列です。
このページのURLの末尾に「?test=abc&hoge=foo」と付け、アクセスしてみましょう。
https://chuna.tech/detail/90/?test=abc&hoge=foo
表示されるページは同じです。
でもURLはしっかり「https://chuna.tech/detail/90/?test=abc&hoge=foo」になっていますね。
この場合のクエリストリングは2つです。
KEY | VALUE |
---|---|
test | abc |
hoge | foo |
この2つのクエリストリングがGETリクエストに乗っかってサーバーへ渡されています。
実際にどんなリクエストがされたか確認してみましょう。
Chromeブラウザは右クリックから「検証」という機能を使うことができます。
リクエストの情報を見てみると、クエリストリングとしてちゃんとデータが渡されていることが確認できました。
さて、無事サーバーにデータが渡されました。
せっかくだからそのデータを使って何かしてみたくないですか?
そこで出てくるのが「HttpRequestオブジェクト」です。
request(HttpRequestオブジェクト)にはクエリストリングの情報も保存されています。
ビューの中で取り出してみたいと思います。
views.py
def IndexView(request):「request」から様々な方法でデータを取得することが可能です。
# requestからクエリストリングをそのまま取り出す
meta_querystring = request.META["QUERY_STRING"] # >> "test=abc&hoge=foo"
# クエリストリングを辞書ライクなオブジェクトとして取得
dict_like = request.GET
# 辞書から"KEY"を指定して"VALUE"を取り出す
value_01 = dict_like["test"] # >> "abc"
value_02 = dict_like["hoge"] # >> "foo"
# 辞書から取り出す別の方法(こちらを推奨)
value_03 = dict_like.get("test") # >> "abc"
value_04 = dict_like.get("hoge") # >> "foo"
# 辞書から全ての"KEY"と"VALUE"を取り出す
for key, value in dict_like.items():
print(key + ":" + value)
# >> test:abc
# >> hoge:foo
ここで1点「request.GET」について。
突然でてきましたので、Djangoの公式ドキュメントから説明を抜粋します。
HttpRequest.GET
渡された HTTP GET パラメータを含む、ディクショナリライクのオブジェクトです。後述の QueryDict のドキュメントを参照してください。
class QueryDict
HttpRequest オブジェクト内では、GET と POST 属性は django.http.QueryDict のインスタンスです。これは、同一のキーに対する複数の値を扱うためにカスタマイズされた、辞書型に似たクラスです。これが必要なのは、いくつかの HTML (特に <select multiple>) が同一キーで複数の値を渡すからです。
簡単に言うと、
GETリクエストの場合は「クエリストリング」の内容を「request.GET」というインスタンスを使って”辞書っぽく”使えるようにしています。
POSTリクエストの場合は「リクエストボディ」の内容を「request.POST」というインスタンスを使って”辞書っぽく”使えるようにしています。
「辞書っぽく」という言葉が引っかかりますね。
ドキュメントによるとQueryDictは、
された特殊な辞書とのこと。同一のキーに対する複数の値を扱うためにカスタマイズ
これはどういうことかと言うと、下記のようなクエリストリングがあり得るという話。
「?test=abc&hoge=foo&test=fuga」
"test"というキーが重複しています。
純粋なPythonの辞書型の場合、1つのキーに対し1つの値しか登録できません。
Djangoでは上記クエリストリングのように重複が発生する場合がある為、”辞書ライクなオブジェクト”を使うことで対策しています。
上記のように重複したキーを持った辞書からデータを取り出してみましょう。
value = request.GET.get("test") # "test"というキーで取り出す"abc"と"fuga"の2つの値があるはずなのに、片方しか取得できていませんね。
print(value)
>> "fuga"
DjangoのQueryDictに対してget()メソッドを使うと、その"KEY"に対して最後に登録された"VALUE"しか返してくれません。
ですので、request.GET.get("test")は"fuga"になってしまいます。
値を全て取り出したい場合は別のメソッドを使用します。
values = request.GET.getlist("test")getlist()というメソッドを使うと、"KEY"が重複している場合の"VALUE"全てをリスト形式で取得できます。
print(values)
>> ['abc', 'fuga'] # リストで取得
2.2 - POSTリクエストの場合
データの保存される場所は異なりますが、扱い方は同じです。
POSTリクエストが送信される場合の例としては、WEBページのフォームに何か情報を入力して「送信ボタン」を押すシチュエーションが挙げられます。
フォームに入力されたデータはリクエストボディという場所に入った状態でWEBサーバーに渡されます。
Djangoは便利なので、データの保存場所がGETと異なっていても同じような方法で取得することができます。
views.py
def IndexView(request):POSTの場合の"KEY"にあたるのは<input>タグの「name」属性です。
# リクエストボディを辞書ライクなオブジェクトとして取得
dict_like = request.POST
# 辞書から"KEY"を指定して"VALUE"を取り出す
post_value = dict_like.get("KEY") # >> "VALUE"
上記はname属性が「KEY」のフォームに「VALUE」と入力して送信ボタンを押した場合に相当します。
template.html
<input type="text" name="KEY">
もうなんとなく分かると思いますが、name属性が重複しているフォームが複数ある場合はgetlist()メソッドを使いましょう。
get()メソッドでは一番最後のフォームの値しか取得できません。
3. まとめ
簡単ではありますがDjangoのrequestについて解説させて頂きました。
HttpRequestからデータを取得する方法はAjaxでよく使います。
取り出し方が分からなくなったらもう一度見に来て下さい。
お疲れ様でした。