
【番外編】Responderを使ってDjangoチュートリアルをやってみた【追加でアプリ改良】
2020.08.06
番外編~Responderを使ってDjangoチュートリアルをやってみた~
今回の記事は、「【第7回】Responderを使ってDjangoチュートリアルをやってみた【adminページ改良編】」の続きにあたります。
第1回から第7回まで読んでいない方は、ぜひ【第1回】から読んでくださいね!
今回の記事は、「Djangoチュートリアル」には載っていませんが、私が第7回までに作ってきたアプリケーションを改良していきたいと思います!
「Webアプリケーションとして、さらにユーザに満足してもらうには?」
「Webアプリケーションに他に必要なものは?」
など様々な視点で改良していきます。
第1回はこちら
こちらの記事もオススメ!
フッターの設定
まず、最初はフッターの設定からやっていきましょう。
これは、Responderとはあまり関係ありません。
しかし、Webアプリケーションを作成する上で欠かせませんのでしっかりと設定しましょう。
templates/layout.htmlのメインコンテンツ下部にフッターを作成
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 | <!-- templates/layout.html --> <!DOCTYPE html> <html lang="ja"> <head> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <link rel="stylesheet" href="{{ static + 'style.css' }}"> <!-- <link rel="stylesheet" href="{{ 'style.css' | static}}">--> <meta charset="UTF-8"> <title>Sample Polls Application</title> </head> <body style="background-color: #eeeeee"> <div class="container"> {% block content %} <!-- メインコンテンツ --> {% endblock %} </div> <!-- New ここから--> <br> <hr> <footer> <br> <div align="center"> <p>Copyright © RightCode Inc. All right reserved.</p> <br> </div> <!-- New ここまで--> </body> </html> |
上のは例ですが、「コピーライト」や「サイトマップ」を入れるのが普通だと思います。
ただ、これでは、質問が少ない時にフッターが上に位置してしまい、なんだかダサいですね・・・
修正
そこで、メインコンテンツに以下のようなスタイルを適用してあげます。
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 | <!-- templates/layout.html --> <!DOCTYPE html> <html lang="ja"> <head> {% set static = '/static/' %} <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <link rel="stylesheet" href="{{ static + 'style.css' }}"> <!-- <link rel="stylesheet" href="{{ 'style.css' | static}}">--> <meta charset="UTF-8"> <title>Sample Polls Application</title> </head> <body style="background-color: #eeeeee"> <div style="display: flex; flex-direction: column; min-height: 80vh"> <!-- New --> <div class="container"> {% block content %} <!-- メインコンテンツ --> {% endblock %} </div> </div> <!-- New --> <br> <hr> <footer> <br> <div align="center">Copyright © RightCode Inc. All right reserved.</div> <br> </footer> </body> </html> |
<div style="display: flex; flex-direction: column; min-height: 80vh"> の部分です。
確認
これで、最低でも80vh分のメインコンテンツ領域が確保されます。
(本当は直書きじゃなくてCSSに書いた方が良いです)
投票結果をグラフ化して表示してみる
今までの投票結果ページは、各選択肢に何票集まったかを表示させるだけの「質素なビュー」でした。
しかし、せっかくPythonでWebアプリを開発しているのですから、matplotlibなど便利なモジュールを使ってスマートにしてみましょう!
まず、投票結果から得たものを棒グラフとして描画します。
棒グラフとして描画
今回は、グラフをSVGとして保存し、それをビューに渡す、というような処理にします。
(なぜPNGなどの拡張子にしないかというと、クッキーの関係で画像変更が、すぐにはブラウザ上で反映されないからです)
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 | @api.route('/result/{q_id}') class Result: async def on_get(self, req, resp, q_id): question = db.session.query(Question).filter(Question.id == q_id).first() choices = db.session.query(Choice).filter(Choice.question == q_id).all() db.session.close() """ ここから New """ # ファイルの保存先とファイル名 file = 'static/images/q_' + str(question.id) choice_text_list = [choice.choice_text for choice in choices] choice_vote_list = [int(choice.votes) for choice in choices] sum_votes = sum(choice_vote_list) vote_rates = [float(vote / sum_votes) for vote in choice_vote_list] # タイトルと棒グラフ描画 plt.title(question.question_text + ' (Total ' + str(sum_votes) + ' votes)') plt.bar(choice_text_list, choice_vote_list, color='skyblue') # 割合と投票人数を表示する # 有効桁数は小数点第1位までにする。 for x, y, v in zip(choice_text_list, vote_rates, choice_vote_list): plt.text(x, v, str(v)+' votes\n' + str(round(y*100, 1))+'%', ha='center', va='bottom') # テキストが被らないように、y軸上限は常に最大投票数の1.2倍にしておく plt.ylim(0, round(max(choice_vote_list)*1.2)) plt.savefig(file + '.svg', format='svg') plt.close() # matplotlibによって保存されたsvgファイルはHTMLで展開する際、冒頭4行はいらない (注: あまりよくないコーディング) # いるのは<svg>タグ内のみ svg = open(file + '.svg', 'r').readlines()[4:] """ ここまで """ resp.content = api.template('result.html', question=question, choices=choices, svg=svg) # svgもビューに渡す |
ちょっと工夫して描画する棒グラフは、少し凝ったものにしてみました。
受け取ったsvgをビューで展開
次に、受け取ったsvgをビューで展開します。
このとき、 < や > が勝手に < や > にエスケープされないように Jinja2 の {% autoescape false %} を使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | {% extends "layout.html" %} {% block content %} <br> <a href="/" class="btn btn-primary">戻る</a> <h2>{{question['question_text']}}</h2> <hr> <p>質問公開日:{{question['pub_date']}}</p> <br> <h3>現在の投票結果</h3> {% for choice in choices %} <p><span class="badge badge-info">{{choice['votes']}}</span> {{choice['choice_text']}}</p> {% endfor %} <!-- ここからNew --> {% for line in svg %} {% autoescape false %}{{line}}{% endautoescape %} {% endfor %} <!-- ここまで --> {% endblock %} |
確認
描画するとこんな感じです。
(matplotlib ver. 3.1.1 での表示)
とても COOL ですね!
APIドキュメントを作ってみる
APIドキュメントとは、その名の通りAPIの使い方を示すドキュメントです。
もし、外部のWebページから本アプリケーションを操作したり、連携したりする場合はAPIを公開する必要があります。
(GoogleカレンダーやGoogleマップなどが身近ですね)
その際、「利用規約や利用方法など細かくAPIについてまとめたもの」、すなわちAPIドキュメントが必要です。
APIドキュメントを作成するときに、最近では「Swgger(OpenAPI)」と呼ばれるフレームワークがよく利用されます。
Responderでは、このSwagger記法に則ったAPIドキュメントが簡単に作成できます。
本アプリケーションは、特にオープンAPIとして開発しているわけではありませんが、Responderの機能ということで紹介します。
最低限の必要な設定
一番最初に書いた、アプリケーションのAPIを作成する部分がありました。
1 | api = responder.API() |
このAPIクラスに引数として必要事項を渡すことで、APIドキュメントを生成してくれます。
早速やってみましょう。
以下のように加筆してみてください。
1 2 3 4 5 6 7 8 9 10 11 12 | api = responder.API( title='Polls Application with Responder', version='1.0', openapi='3.0.2', docs_route='/docs', description='This is a simple polls application referenced Django tutorials with Responder 1.3.1.', contact={ 'name': 'RightCode Inc. Support', 'url': 'https://rightcode.co.jp/contact', 'email': '****@abcdefg.com' } ) |
引数の名前から何を設定しているかはわかると思います。
OpenAPIは、現在(2019/10/15)の最新版は「3.0.2」なので、 openapi='3.0.2' としています。
APIドキュメントのルーティングは、 docs_route='/docs' です。
確認
では、早速サーバを立ち上げて、127.0.0.1:5042/docs にアクセスしてみましょう。
Swagger によって、 APIドキュメントの大枠が作成されました!
ちなみに、Swagger のソースは、http://127.0.0.1:5042/schema.yml にあります。
モデルのスキーマ(構造)の追加
まずは、モデルのスキーマからドキュメントに入れていきましょう。
まず、コードが乱雑にならないように、新しく shemas.py というファイルを作りましょう。
そうしたら、各モデルに対してフィールド情報を「marshmallow(マシュマロ)」というライブラリを使って定義していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | from urls import api from marshmallow import Schema, fields @api.schema('Question') class QuestionSchema(Schema): id = fields.Integer() question_text = fields.Str() pub_date = fields.DateTime() @api.schema('Choice') class ChoiceSchema(Schema): id = fields.Integer() question = fields.Integer() choice_text = fields.Str() votes = fields.Integer() @api.schema('User') class UserSchema(Schema): id = fields.Integer() username = fields.Str() password = fields.Str() |
このあたりは、ほぼ Responder のドキュメント通りです。
あとは、このファイルを urls.pyでインポートします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # .. 省略 api = responder.API( title='Polls Application with Responder', version='1.0', openapi='3.0.2', docs_route='/docs', description='This is a simple polls application referenced Django tutorials with Responder 1.3.1.', contact={ 'name': 'RightCode Inc. Support', 'url': 'https://rightcode.co.jp/contact', 'email': '****@abcdefg.com' } ) # ModelSchemaをimport import schemas # ... 省略 |
早速、もう一度サーバを立ち上げ直して、ドキュメントを見てみましょう!
モデルスキーマがドキュメントに追加されました!
URLスキーマの追加
あとは、URLによるレスポンスなどを追加してみましょう。
試しに、ルート("/")でやってみます。
今回は、かなり簡単な記述ですが、気になる方は Swagger の記法を調べてみてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 | @api.route('/') class Index: """ --- get: description: Get Question list except future questions responses: 200: description: Success """ def on_get(self, req, resp): # ... 省略 def get_queryset(self, latest=5): # ... 省略 |
これで、サーバを立ち上げ直してドキュメントを確認してみます。
確認
実際に「Try it out」して実行(Execute)してみると、インデックスビューのボディがレスポンスされるのをWebページ上から確認できます。
URLスキーマのサンプル
次に、GETメソッドとPOSTメソッドによって、処理の違うログイン("/ad_login")についてのスキーマの例を書いてみます。
あまり、有用ではありませんが、記述方法の参考になれば幸いです。
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 | @api.route('/ad_login') class AdLogin: """ --- get: description: Redirect Login view (admin.html) post: description: If login successes, redirect admin page view (administrator.html). parameters: - name: username in: body required: true description: username schema: type: strings properties: username: type: string example: hogehoge - name: password in: body required: true description: password schema: type: strings properties: password: type: string example: a1B2c3D4e5 responses: 200: description: Redirect administrator page. """ |
確認
最低限必要な構成要素は、
1 2 3 4 5 | """ # ここにはコメントなど --- # ここにSwagger(yml)を記述 """ |
でしょう。
さいごに【番外編】
Djangoチュートリアルを一通り終え、さらにその先までアプリ作成してみました。
Responder の良い部分、使いにくい部分など、様々あるかと思います。
Responder は、まだまだ発展途上なフレームワークと言えると思います。
しかし、これからのアップデートで、さらに便利な機能がデフォルトで追加されると思うので、楽しみですね。
今回、作成したアプリのソースは、Githubにあげています。
【ソースはこちら】
https://github.com/rightcode/ResponderTutorial
少しだけディレクトリ構成などが異なっていて、本連載の「responder」ディレクトリは「ResponderTutorial/polls」にあたりますのでご注意を!
連載終了!
これで「Responderを使ってDjangoチュートリアルをやってみた」の連載は終了となります!
全8回に渡ってご愛読いただいたみなさま、ありがとうございました!
ぜひ、当ブログの他の記事も読んでみてくださいね!
【全編まとめ】Responderを使ってDjangoチュートリアルをやってみた
こちらの記事もオススメ!
ライトコードよりお知らせ






一緒に働いてくれる仲間を募集しております!
ライトコードでは、仲間を募集しております!
当社のモットーは「好きなことを仕事にするエンジニア集団」「エンジニアによるエンジニアのための会社」。エンジニアであるあなたの「やってみたいこと」を全力で応援する会社です。
また、ライトコードは現在、急成長中!だからこそ、あなたにお任せしたいやりがいのあるお仕事は沢山あります。「コアメンバー」として活躍してくれる、あなたからのご応募をお待ちしております!
なお、ご応募の前に、「話しだけ聞いてみたい」「社内の雰囲気を知りたい」という方はこちらをご覧ください。
ライトコードでは一緒に働いていただける方を募集しております!
採用情報はこちら書いた人はこんな人

IT技術2021.01.26【Python】Tkinterで簡易的な電卓作ってみた!
ITエンタメ2021.01.12【スティーブ・ウォズニアック】アップルコンピューターのもう一人の創業者!
IT技術2021.01.11React Hooks登場でコンポーネントはどう変わった?
IT技術2021.01.05【Unity】Rigidbodyの基本