エディター
スタイルの編集
- File→Settings→Themes
- 冒頭”your stylesheet”をクリック
- styles.lessの内容を編集
コメントの色
styles.lessの以下の部分を編集(color
の値はテーマによる)。
1 2 3 |
atom-text-editor::shadow .comment { color: forestgreen } |
その他
VagrantにSFTP接続
styles.lessの以下の部分を編集(color
の値はテーマによる)。
1 2 3 |
atom-text-editor::shadow .comment { color: forestgreen } |
VagrantにSFTP接続
Python3で指定したURLのサイトからレスポンスを得る手順は以下のとおり。
urllib.request.urlopen
で接続のオブジェクトを得るread
メソッドを実行するただし結果はバイト列で得られる。
以下はurlopen
の引数にURLを指定した手順。
urlopen
の戻り値(Response
オブジェクト)を変数response
に保存response
のread
メソッドメソッドでレスポンスの内容を取得レスポンスの内容はバイト列で返される。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import urllib.request # URLのセット url = 'http://www.httpbin.org' # 指定したURLで接続 with urllib.request.urlopen(url) as response: # レスポンスを取得 html = response.read() # レスポンスの内容を表示 print(html) # b'<!DOCTYPE html>\n<html lang="en">\n\n<head>\n <meta charset="UTF-8">\n .... |
文字列でレスポンスを得るには、decode
メソッドでバイト列を文字列にデコードする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import urllib.request url = 'http://www.httpbin.org' with urllib.request.urlopen(url) as response: html = response.read().decode('utf-8') print(html) # <!DOCTYPE html> # <html lang="en"> # # <head> # <meta charset="UTF-8"> # <title>httpbin.org</title> # .... |
まずURLを指定してurllib.request.Request
オブジェクトを生成し、これをurlopen
の引数として接続を開く。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import urllib.request # URLをセット url = 'http://www.httpbin.org' # URLを指定してRequestオブジェクトを生成 request = urllib.request.Request(url) # Requestオブジェクトを指定して接続 with urllib.request.urlopen(request) as response: # レスポンスの内容を取得し、UTF-8でデコード html = response.read().decode('utf-8') # レスポンスの内容を表示 print(html) # <!DOCTYPE html> # <html lang="en"> # # <head> # <meta charset="UTF-8"> # <title>httpbin.org</title> # .... |
ポストの流れは以下のとおり。
urllib.request
とurllib.parse
をインポートするurlopen
の引数にRequest
オブジェクトを渡すRequest
オブジェクト生成時にdata
引数を指定するとPOST
メソッドになる(method='POST'
がなくてもよい)以下はhttpbin.org
を利用して確認した例。httpbin.org/post
からのレスポンスがJSON形式なので、json
ライブラリーをインポートしてレスポンスを整形している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import json import urllib.parse import urllib.request # URLを設定 url = 'http://www.httpbin.org/post' # POSTで送るパラメーターを設定 parameters = { 'name': 'tau', 'site': 'taustation.com', } # 辞書で設定したパラメーターをパース・エンコード parsed_data = urllib.parse.urlencode(parameters).encode('utf-8') # data引数にパラメーターを指定してRequestオブジェクトを生成 request = urllib.request.Request(url, data=parsed_data) # Requestオブジェクトをurlopenに渡して接続 with urllib.request.urlopen(request) as response: # レスポンスをUTF-8でデコードして取得 jsontext = response.read().decode('utf-8') # httpbin.org/postのレスポンスはJSONなので整形して表示 decoded = json.loads(jsontext) encoded = json.dumps(decoded, ensure_ascii=False, indent=4) print(encoded) # { # "args": {}, # "data": "", # "files": {}, # "form": { # "name": "tau", # "site": "taustation.com" # }, # "headers": { # "Accept-Encoding": "identity", # "Content-Length": "28", # "Content-Type": "application/x-www-form-urlencoded", # "Host": "www.httpbin.org", # "User-Agent": "Python-urllib/3.6", # "X-Amzn-Trace-Id": "Root=1-62b8487d-6f1d1dee5fe6eb1e2e371679" # }, # "json": null, # "origin": "106.155.2.90", # "url": "http://www.httpbin.org/post" # } |
JSONの文字列が与えられたときの扱い。
json.loads
で文字列をPythonの変数に変換(デコード)json.dumps
でPythonの変数を文字列に変換(エンコード)
ensure_ascii=False
でユニコード文字をエスケープさせないindent=n
でJSONの構造に即した改行・インデントを適用json.loads
メソッドは、JSON文字列をPythonの変数にデコードする。Pythonの辞書になっていることがわかる。
1 2 3 4 5 6 7 8 9 10 |
mport json jsontext = '{ "要素1": "値1", "要素2": { "要素2-1": "値2-1" } }' decoded = json.loads(jsontext) print(type(decoded)) print(decoded) # <class 'dict'> # {'要素1': '値1', '要素2': {'要素2-1': '値2-1'}} |
JSONのキーと値が辞書のキーと値に対応している。
json.dumps
は、辞書に変換されたJSONの内容を文字列にエンコードする。
ただしこのとき、ユニコードはエスケープされ、改行を含まない文字列となる。
1 2 3 4 5 6 7 8 9 10 11 12 |
mport json jsontext = '{ "要素1": "値1", "要素2": { "要素2-1": "値2-1" } }' decoded = json.loads(jsontext) encoded = json.dumps(decoded) print(type(encoded)) print(encoded) # <class 'str'> # {"\u8981\u7d201": "\u50241", "\u8981\u7d202": {"\u8981\u7d202-1": "\u50242-1"}} |
以下の2つの引数を指定する。
ensure_ascii=False
indent=n
これによって、ユニコード文字列はエスケープされず、JSONの構造に即した改行・インデントが適用された文字列が得られる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import json jsontext = '{ "要素1": "値1", "要素2": { "要素2-1": "値2-1" } }' decoded = json.loads(jsontext) encoded = json.dumps(decoded, ensure_ascii=False, indent=4) print(encoded) # { # "要素1": "値1", # "要素2": { # "要素2-1": "値2-1" # } # } |
JSONの文字列が与えられたときの扱い。
json_decode
で文字列をPHPの変数に変換json_encode
でPHPの変数を文字列に変換
JSON_UNESCAPED_UNICODE
とJSON_PRETTY_PRINT
のフラグを立てるjson_decode
関数は、引数で与えられたJSON文字列をPHPの変数に変換する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php $jsontext = '{ "要素1": "値1", "要素2": { "要素2-1": "値2-1" } }'; $decoded = json_decode($jsontext); var_dump($decoded); // object(stdClass)#1 (2) { // ["要素1"]=> // string(4) "値1" // ["要素2"]=> // object(stdClass)#2 (1) { // ["要素2-1"]=> // string(6) "値2-1" // } // } |
JSONのキーや単体の要素はstring
に、{ ... }
はstdClass
のオブジェクトに変換され、キー=>値がオブジェクトのプロパティーとなっている。
json_encode
は、PHPの変数で表現されたJSONコードを文字列に変換する。
ただしこのとき、ユニコードはエスケープされ、改行を含まない文字列となる。
1 2 3 4 5 6 7 |
<?php $jsontext = '{ "要素1": "値1", "要素2": { "要素2-1": "値2-1" } }'; $decoded = json_decode($jsontext); echo json_encode($decoded). PHP_EOL; // {"\u8981\u7d201":"\u50241","\u8981\u7d202":{"\u8981\u7d202-1":"\u50242-1"}} |
以下の2つのフラグをセットする。
JSON_UNESCAPED_UNICODE
JSON_PRETTY_PRINT
これによって、ユニコード文字列はエスケープされず、JSONの構造に即した改行・インデントが適用された文字列が得られる。
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php $jsontext = '{ "要素1": "値1", "要素2": { "要素2-1": "値2-1" } }'; $decoded = json_decode($jsontext); echo json_encode($decoded, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT). PHP_EOL; # { # "要素1": "値1", # "要素2": { # "要素2-1": "値2-1" # } # } |
Pythonでの文字列表現をバイト列に変換するにはencode
メソッドを用い、バイト列を文字列に変換するにはdecode
メソッドを用いる。encode
/decode
の引数には文字コードを指定する。
バイト列をPythonのprint
文で出力すると、b'文字列'
で表現され、改行文字などはエスケープコード(\n
など)で表示される。
ASCII文字列はencode
/decode
の引数を'ascii'
とする。
1 2 3 4 5 6 7 8 9 10 |
# String->bytesへエンコード s = 'ABC\nDEF' print(s.encode('ascii')) # b'ABC\nDEF' # bytes->Stringへデコード b = b'ABC\nDEF' print(b.decode('ascii')) # ABC # DEF |
ASCIIの場合は引数が'utf-8'
や'shift_jis'
としても結果は同じ。
bytes.hexメソッドの引数にバイト列を与えると、その16進表現の文字列が得られる。
1 2 |
print(bytes.hex(b'ABC\nDEF')) # 4142430a444546 |
マルチバイト文字の場合、encode
とdecode
で文字コードを整合させる。
1 2 3 4 5 6 7 8 9 10 |
# String->bytesへエンコード s = 'あい\nうえ' print(s.encode('utf-8')) # b'\xe3\x81\x82\xe3\x81\x84\n\xe3\x81\x86\xe3\x81\x88' # bytes->Stringへデコード b = b'\xe3\x81\x82\xe3\x81\x84\n\xe3\x81\x86\xe3\x81\x88' print(b.decode('utf-8')) # あい # うえ |
encode
とdecode
で文字コードが違うと、文字化けするのではなくエラーになる。
1 2 3 4 5 6 |
b = b'\xe3\x81\x82\xe3\x81\x84\n\xe3\x81\x86\xe3\x81\x88' print(b.decode('shift_jis')) # Traceback (most recent call last): # File "bytestring.py", line 5, in <module> # print(b.decode('shift_jis')) # UnicodeDecodeError: 'shift_jis' codec can't decode byte 0x86 in position 9: illegal multibyte sequence |
ただしerrors
引数の設定をデフォルトの'strict’
から変更すると、文字化けした文字列などが返される。
マルチバイト文字の16進表現はバイト列の表現のとおりになる。
1 2 |
print(bytes.hex(b'\xe3\x81\x82\xe3\x81\x84\n\xe3\x81\x86\xe3\x81\x88')) # e38182e381840ae38186e38188 |
大まかな流れは以下のとおり。
open
close
具体的には、open
でファイルオブジェクトを取得し、読み書きやclose
などの操作はファイルオブジェクトに対して実行。
以下のファイルを準備。行末は全て改行コードで終わっている。
1 2 3 |
When the dog bytes When the bee stings When I'm feeling sad |
以下のコードは、ファイルを読み込みモードで開き、内容を一つの文字列に一括して読み込んで表示している。
1 2 3 4 5 6 7 8 9 10 11 |
# 読み込みモードでファイルをオープン f = open('test.txt', 'r') # ファイルの内容を一括読み込み text = f.read() # ファイルをクローズ f.close() # 読み込んだファイル内容を表示 print(text) |
実行結果は以下のとおり。
1 2 3 4 5 6 |
$ python3 filetest.py When the dog bytes When the bee stings When I'm feeling sad $ |
ファイルの各行末に改行コードがあるため、そこで改行されて表示される。さらにprint
関数で自動的に改行コードが付加されるため、最後に空白行が入っている。
with
文でopen
を指定することにより、with
ブロック終了時に自動的に終了処理(close
)が実行される。
1 2 3 4 5 6 7 |
# 読み込みモードでファイルを使う with open('test.txt', 'r') as f: # ファイル内容を行に分割してリストに格納 text = f.read() # リストの要素=各行を表示 print(text, end='') |
read
メソッドはファイルの全内容を一つのテキストとして読み込む。
1 2 3 4 5 6 7 8 9 |
# ファイル内容を行に分割してリストに格納 text = f.read() # リストの要素=各行を表示 print(text, end='') # When the dog bytes # When the bee stings # When I'm feeling sad |
readline
は実行のたびにファイルから1行ずつ読み込み、ポインターを次の行に進める。読み込む内容がない場合には''
を返す。
以下はファイルの内容を2行だけ読みだす例。
1 2 3 4 5 6 7 8 |
// 1行ずつファイルを読み込みながら表示 line = f.readline() print(line, end='') line = f.readline() print(line, end='') # When the dog bytes # When the bee stings |
以下はファイル内容の全行を1行ずつ取り出す例。
1 2 3 4 5 6 7 8 9 |
while True: text = f.read() if text == '': break print(text, end='') # When the dog bytes # When the bee stings # When I'm feeling sad |
readlines
はファイルの内容を行に分解し、各行の内容を要素としたリストを返す。
1 2 3 4 5 6 7 8 9 10 |
# ファイル内容を行に分割してリストに格納 lines = f.readlines() # リストの要素=各行を表示 for line in lines: print(line, end='') # When the dog bytes # When the bee stings # When I'm feeling sad |
ファイルに書き込む場合、オプションに'w'
を指定。既にファイルが存在する場合、その内容は無視されて上書きされる。
write
文は引数の文字列を単に書き込む。
1 2 3 4 5 6 7 8 9 |
# 読み込みモードでファイルをオープン with open('test.txt', 'w') as f: # 1行ずつファイルへ書込み f.write('Raindrops on roses and whiskers on kittens\n') f.write('Bright copper kettles and warm woolen mittens\n') # test.txtの内容 # Raindrops on roses and whiskers on kittens # Bright copper kettles and warm woolen mittens |
print
文のfile
引数にファイルオブジェクトを指定すると、ファイルに書き込むことができる。この場合、書式設定などを活用することができる。
1 2 3 4 5 6 7 |
with open('test2.txt', 'w') as f:# 1行ずつファイルへ書込み print('1 million yen', file=f) print('{:,} yen'.format(1000000), file=f) # test2.txtの内容 # 1 million yen # 1,000,000 yen |
既存のファイルに追記するには'a'
オプションを指定。以下の例では、上で作成されたtest.txt
に1行追記している。
1 2 3 4 5 6 7 8 9 |
# 読み込みモードでファイルをオープン with open('test.txt', 'a') as f:# 1行ずつファイルへ書込み # 1行追記 f.write('Brown paper packages tied up with strings\n') # test.txtの内容 # Raindrops on roses and whiskers on kittens # Bright copper kettles and warm woolen mittens # Brown paper packages tied up with strings |
ファイルが存在しない場合は、'w'
と同様にファイルが作成されて新規に書き込みされる。
1 2 3 4 5 6 |
$ rm test.txt $ ls filetest.py $ python3 filetest.py $ cat test.txt Brown paper packages tied up with strings |
このページはDjangoのDocumentation、Writing your first Django app, part 4に対応している。
チュートリアルのテンプレートのところで作成したindex
ページでは、データベースのquestionテーブルに保存されたQuestion
データの一覧が表示される。
各Questionデータのリンクをクリックすると、polls.views
モジュールのdetail
関数にルーティングされ、detail
テンプレートが呼ばれてページが表示される。
現状でこのページでは、"You're looking at question 1."
のようにクリックしたQuestion
データのid
を含むテキストが表示されるだけ。
ここでdetail
ページに以下のような機能を持たせる。
Question
データのquestion_text
を表示するQuestion
データに関連付けられた(すなわちその回答選択肢となる)Choice
データ群を、ラジオボタンとともに表示するVote
ボタンを押すと、選択した回答のid
が送信されるid送信後の振る舞いについては後に実装することとして、ここでは上の機能をテンプレートで実装していく。
polls
アプリケーションのdetail
テンプレートを以下の内容に変更する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<form action="{% url 'polls:vote' question.id %}" method="post"> {% csrf_token %} <fieldset> <legend><h1>{{ question.question_text }}</h1></legend> {% if error_message %} <p><strong>{{ error_message }}</strong></p> {% endif %} {% for choice in question.choice_set.all %} <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}"> <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label> {% endfor %} </fieldset> <input type="submit" value="Vote"> </form> |
question
オブジェクトとerror_message
(テキスト)が渡されることを想定している(上記コードのL4, 6, 8)。
form
要素のオプション設定は以下の通り。
action
の呼び出し先はpolls
アプリケーションのname='vote'
ルート→vote
関数で、question
データのid
をURLパラメーターで渡すPOST
form
要素内の最初にCSRF/XSRF (cross-site request forgery)対策として{% csrf_token %}
を置いている。form
要素にこのタグを置かないと、Djangoによりアクセス禁止のエラーとなる。
ビューから受け取ったquestion
オブジェクトのquestion_text
プロパティーの内容をh1要素として表示させる。
ビューの処理でエラーになった場合error_message
変数が定義されることを前提としている。DTLのif
タグにより、error_message
がセットされているときはこれが表示され、選択肢とラジオボタンが表示される。
question
データに対する選択肢はChoice
データが関連付けられていて、そのコレクションはDTL
のドット検索によりquestion.choice_set.all
で得られる。
フォームでは、このコレクションの要素(各Choice
データ)をDTLのfor
タグで1つずつ取出している。そして、各choice
についてラジオボタンとラベルテキストを生成している。
ラジオボタンの属性は以下のとおり。
name
属性を適用してグループ化している。label
要素で参照するid
で、先頭から連番でchoice1
, choice2
, …のような値となる。forloop.counter
はDTLのfor
タグの変数で、ループカウンターの値が得られる。Choice
データのid
が充てられる。このid
によって、データベース上の指定したデータが得られる。このテンプレートがレンダリングされたときのHTMLは以下のとおり。value
の値が1, 2, 4となっているが、データの削除・追加によってAUTO_INCREMENT
の値は必ずしも連続にならない。一方、forloop.counter
を利用したidの数字部分は連番となっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<form action="/polls/1/vote/" method="post"> <input type="hidden" name="csrfmiddlewaretoken" value="AkC8p5sJzHblsYxvYtn124PmXPt9u9SC8qJqa6QMinL8MzCuiMsMmAhrCfHuR1YZ"> <fieldset> <legend><h1>What's up?</h1></legend> <input type="radio" name="choice" id="choice1" value="1"> <label for="choice1">Not much</label> <input type="radio" name="choice" id="choice2" value="2"> <label for="choice2">The sky</label> <input type="radio" name="choice" id="choice3" value="4"> <label for="choice3">Just hacking again</label> </fieldset> <input type="submit" value="Vote"> </form> |
ここでindex
ページのQuestion
データのうち"What's up?"
をクリックすると、レンダリングされたdetail.html
により以下のページが表示される。
この段階でVoteボタンを押すと、polls.views
モジュールのvote
関数にルーティングされるが、この段階では”You’re voting on question 1.”とだけ表示される。
1 2 |
def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id) |
ここではフォームのアクション先であるvote
関数の処理を実装する。処理内容は、Question
の持つ選択肢のうちditail
ページで選択・送信されたChoice
データのvote
カウンターを1つ増やしてデータベースを変更するというもの。
vote
関数の内容を以下のように変更する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render, get_object_or_404 from django.urls import reverse from .models import Choice, Question .... def vote(request, question_id): question = get_object_or_404(Question, pk=question_id) try: selected_choice = question.choice_set.get(pk=request.POST['choice']) except (KeyError, Choice.DoesNotExist): # エラーメッセージを加えて元の内容を表示 return render(request, 'polls/detail.html', { 'question': question, 'error_message': "You didn't select a choice.", }) else: selected_choice.votes += 1 selected_choice.save() # POSTデータが正常に処理された場合、HttpResponseRedirectを返す。こにより # ユーザーが戻るボタンを押しても繰り返してデータが投稿されるのを防ぐ return HttpResponseRedirect(reverse('polls:results', args=(question_id,))) |
パッケージ先頭でHttpResponseRedirect
モジュールをインポートしている。このモジュールは、URLconfで定義したルート名とパラメーターを与えてレスポンスを返す。
またChoice
モデルを扱うために、Question
に加えてChoice
モジュールもインポートしている。
フォームのaction
属性で、Question
データのid
をクエリーパラメーターで渡すようになっていて、vote
関数の引数でこれを受け取る。
そのid
でQuestion
データを取得し、存在しなければ404エラーとなる。
このブロックで、Question
データに対する選択肢をデータベースから取得し、エラーならエラー処理をし、成功したならその選択肢の得票数を1つ増やす。詳細は以下の通り。
try
節では、前段で得られたQuestion
データの選択肢Coice
データのうちフォームで選択されたものを取得する。
get
メソッドでpk
がPOST['choice']
に等しいデータを取り出しているPOST['choice']
はフォームにおいてname='choice'
のINPUT
要素から得られるvalue
属性の内容'choice'
であり、POST['choice']
から選択されたボタンのvalue
属性の値が得られるvalue={{ choice.id }}
としているので、この値は選択された選択肢に対応するChoice
データのid
の値except
節では、引数で指定したKeyError
とChoice.DoesNotExist
の2つの例外をキャッチしている。いずれかの例外が発生した場合に、現在処理中のQuestion
データを与えて元のdetail
ページを表示させるが、このときエラーメッセージをセットしているのでこれも表示される。
KeyError
はPOST['choice']
でデータを取り出すときに'choice'
というキーが存在しない場合に発生する。このような状況は次の場合に発生する。
Question
データに対してVoteボタンを押した場合(今回の例の場合question_text
が"Hou're you doing?"
のデータ)name
属性が改ざんされた場合Choice.DoesNotExist
の方は、get
メソッドで条件に合うデータがデータベース上にないときに発生する。ただ今回のコードでは、DB上のデータが取得できない場合はget_object_or_404
により404エラーへと飛ぶようになっているので、ここでDoesNotExist
例外は発火しないのではないか。
else
節はtry
節の処理が正常終了した場合に実行される。今回の場合、フォームで選択された選択肢のChoice
データが無事取得できたことになるので、データベース上の得票数カウンターを1つ増やして投票結果表示のresult
ページにリダイレクトする。
得票数のカウントアップは簡単で、取得済みのデータについて以下を実行。
1 |
selected_choice.votes += 1 |
ただしこれはデータベースからメモリー上に取得されたデータを変更しただけなので、変更後のデータをデータベースに登録する必要がある。
1 |
selected_choice.save() |
最後に戻り値としてHttpResponseRedirect
を返している。これはDjangoに特化したものではなく、HTTPでの処理の標準的な手筋。
1 2 3 4 5 6 7 8 9 |
mysql> select * from choices; +----+--------------------+-------+-------------+ | id | choice_text | votes | question_id | +----+--------------------+-------+-------------+ | 1 | Not much | 1 | 1 | | 2 | The sky | 0 | 1 | | 4 | Just hacking again | 0 | 1 | +----+--------------------+-------+-------------+ 3 rows in set (0.00 sec) |
ビュー関数/ビュークラスのメソッドはHttpResponse
クラスやその派生クラスのインスタンスを戻り値として、テンプレートをレンダリングしたり指定したURLに遷移する。
この戻り値の与え方としては、HttpResponse
やその派生クラスのインスタンスを直接与える方法と、django.shortcuts
で定義された関数(render
、 redirect
)を使う方法がある。
HttpResponseはレスポンスボディ―を指定して表示。ショートカットのrender
は引数にテンプレートファイルを指定し、それを表示する。
URLを指定したリダイレクトには、HttpResponseRedirect
やショートカットのrender
を使う。
HttpResponse
はdjango.http
パッケージのモジュールで、ヘッダーや内容などを持つHTTPレスポンスのクラス。インスタンス生成時に引数にテキストを指定したテキストがレスポンスボディ―になり、直接表示される。
テキストのほか、イテレーターやファイルも指定できる。
1 2 3 4 |
from django.http import HttpResponse def http_response_text(request): return HttpResponse('<h1>HttpResponse Test</h1>') |
render
はdjango.shortcuts
パッケージのモジュールで、指定したテンプレートファイルの内容をHttpRequest
インスタンスとして返す。
テンプレートファイルに加えて、テンプレートに引き渡す変数コンテキストも指定できる。
1 2 3 4 |
from django.shortcuts import render def target(request): return render(request, 'appname/target.html') |
HttpResponseRedirect
はdjango.http
パッケージのモジュールで、指定したURLに遷移するためのHTTPレスポンスのインスタンスを生成する。
HttpResponseRedirect
はHttpResponseRedirectBase
を継承し、HttpResponseRedirectBase
はHttpResponse
を継承している。
ビュー関数/メソッドの中で、URLを引数に与えてHttpResponseRedirect
のインスタンスを生成し、戻り値にそのインスタンスを渡すことで、指定したURLにリダイレクトされる。
引数のURLは以下のように与えることができる。
urls.py
のpath
関数で設定したルート名をreverse
関数の引数に与えた戻り値(ルート名を直接与えることはできない)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from django.http import HttpResponseRedirect # 絶対URL指定 def http_response_redirect_absolute(request): return HttpResponseRedirect('http://localhost:8000/appname/target') # アプリケーション相対URL指定 def http_response_redirect_relative(request): return HttpResponseRedirect('/appname/target') # urls.pyのルート名もreverse関数を通して指定可能 def http_response_redirect_name(request): return HttpResponseRedirect(reverse('appname:target')) # return HttpResponseRedirect('appname:target') -> ERROR! # 直接ルート名は指定できない |
redirectはdjango.shortcuts
パッケージのモジュールで、指定したURLにリダイレクトするためのHTTPレスポンのためのスインスタンスを返す。
redirect
は関数として定義され、デフォルトではHttpResponseRedirect
のインスタンスを返すが、引数にpermanent=True
を設定すると、HttpResponsePermanentRedirect
のインスタンスを返す(このクラスは301ステータスを返す)。
redirect
がHttpResponseRedirect
を返す際、resolve_url
関数を介していて、reverse
関数を使わず直接ルート名を渡すことができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from django.shortcuts import redirect # 絶対URL指定 def redirect_absolute(request): return redirect('http://localhost:8000/appname/target') # アプリケーション相対URL指定 def redirect_relative(request): return redirect('/appname/target') # ルート名も指定可能 def redirect_name(request): return redirect(reverse('appname:target')) # return redirect('appname:target') -> OK! # ルート名のみを指定してもよい |
HttpResponse
はdjango.http
パッケージにあり、HTTPリクエストに対してサーバーからクライアントに返されるHTTPレスポンスを表すクラス。
Django Documentation – Request and response objects
HttpResponse
は以下のプロパティーを持つ(一部記載)。
まずモジュールをインポートし、文字列を渡してHttpResponse
インスタンスを生成。インスタンス表現や型を確認。
1 2 3 4 5 6 |
>>> from django.http import HttpResponse >>> response = HttpResponse('HTTP content') >>> response <HttpResponse status_code=200, "text/html; charset=utf-8"> >>> type(response) <class 'django.http.response.HttpResponse'> |
ステータスコードとステータスフレーズを確認。
1 2 3 4 |
>>> response.status_code 200 >>> response.reason_phrase 'OK' |
ヘッダーとそのうちの文字コードを確認。
1 2 3 4 |
>>> response.headers {'Content-Type': 'text/html; charset=utf-8'} >>> response.headers['Content-Type'] 'text/html; charset=utf-8' |
ボディー部の確認。バイトストリングになっている。
1 2 |
>>> response.content b'HTTP content' |
テンプレートのフォームで入力・送信されたデータをビューで受け取って処理する方法の基本。
ビュー関数/メソッドの引数(たとえばrequest
)のPOST
配列でフォームのINPUT
要素のname
属性値を指定して値を取得する。
request.POST['name属性値']
urls.py
でinput
ページとresult
ページへのルーティングを設定。input
ページにフォームを置き、そこから送信されたデータをresult
ページで表示することとする。
DTLのurl
タグを使うため、各ルートにname
引数でルート名を付けている。
1 2 3 4 5 6 7 8 9 |
from django.urls import path from formtest import views app_name = 'formtest' urlpatterns = [ path('', views.input, name='input'), path('result', views.result, name='result'), ] |
ルート名formtest:input
でルーティングされるview
関数。単にinput.html
テンプレートをレンダリングしている。
1 2 3 4 |
from django.shortcuts import render def index(request): return render(request, 'formtest/input.html') |
input.html
テンプレート。フォームには、text
タイプのINPUT
要素と、3つの選択肢を持つradio
タイプのINPUT
要素群が置かれている。
text
タイプのINPUT
要素のname
属性はinput_test
radio
タイプの3つのINPUT
要素のname
属性はradio_choice
で値はpine
、bamboo
、plum
送信ボタンが押されると、method
で指定したURLに飛ぶが、ここではurls.py
で定義したformtest:result
に飛ぶようにDTLのurl
タグを使っている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<h1>フォームテスト</h1> <form action="{{ url formtest:result }}" method="post"> {% csrf_token %} {# text #} <div> <input type="text" name="input_text" value=""> </div> {# radio #} <div> <label> <input type="radio" name="radio_choice" value="pine">松 </label> <label> <input type="radio" name="radio_choice" value="bamboo">竹 </label> <label> <input type="radio" name="radio_choice" value="plum">梅 </label> </div> <input type="submit" value="送信"> </form> |
フォームからの送信に対してviews.py
のresult
関数が呼ばれる。result.html
テンプレートにcontext
辞書を介して2つの変数input_text
とradio_choice
を渡している。
input_text
変数には、name='input_text'
で指定されたテキストの内容がrequest.POST['input_text']
で取得されて代入されるradio_choice
変数には、name='radio_choice'
で指定されたラジオボタンのうち選択されたもののvalue
値がrequest.POST[‘radio_choice’]で取得され、これに対応する文字列が代入される
1 2 3 4 5 6 7 8 9 10 11 12 |
from django.shortcuts import render def input(request): return render(request, 'formtest/index.html') def result(request): radio_result = { 'pine': '松', 'bamboo': '竹', 'plum': '梅' } context = { 'input_text': request.POST['input_text'], 'radio_choice': radio_result[request.POST['radio_choice']], } return render(request, 'formtest/result.html', context) |
result
テンプレートでは、ビューのcontext
で設定された変数の内容を表示する。
1 2 3 4 5 6 7 8 9 10 |
<h1>送信結果</h1> <p> 入力されたテキストは"{{ input_text }}"です。 </p> <p> 選択されたのは"{{ radio_choice }}"です。 </p> <a href="{% url 'formtest:input' %}">フォームに戻る</a> |