ヘルスチェックについてよくわかってなかったので勉強しました
はじめに
Fargate で ALB を使う機会があり、ヘルスチェックを設定しました。
ヘルスチェックを実際にやったことがなかったので、調べつつやったのですが、その際に知ったこととかをまとめます。
ヘルスチェックで重要なこと
下記の記事によると、
そのヘルスチェックでどこの範囲の正常性を担保するか
これが重要なようです。
僕は、Fargate で nginx と php のコンテナを立てており、それらのヘルスチェックをする際に、はじめは nginx でヘルスチェックを行っていました。
nginx でのヘルスチェックは、下記の様に empty_gif を返すのが一般的みたいです。
location = /healthcheck.html { empty_gif; access_log off; break; }
ただ、途中で、「これだと、nginx の起動は確認できても、php のコンテナ 死んでてもわからなくない?」って思い、結果的に以下のようにしました。
Laravel を使っていたので、web.php
にヘルスチェック用のパスを作りました。
これで、php の動作確認もできるようになりました!!!
Route::get('/healthcheck', function() { return 'health ok'; });
参考リンク
【読書記録】「なぜ、あなたの仕事は終わらないのか」に学ぶやること・やらないこと
今回はこちらを読みました。
単なる時間術だけでなく、仕事が終わらない根本的原因から、時間術を身につけることによるメリット、実際に時間術を導入する方法がたくさんの具体例やエピソードとともに書かれていてとても読みやすかったです。
以降、自分なりのまとめになります。
速く仕事を終わらすためにやらないこと
ラストスパート志向
仕事が終わらない諸悪の根源。
日本人の多くがラストスパート志向で、締め切り前夜に徹夜をして仕事を終わらせようとしている。
これだと最後までタスクの難易度が分からないし、予期せぬアクシデントにも対応できず、結局期限までに終わらせられないことが多くなる。
締め切り間近は焦るので、スパゲティコードになるし、それを修正する時間もない。締め切り=努力目標という思考
見積もりは所詮見積もりに過ぎず、スケジュール通りに進まないこともあり、締め切りに間に合わなければリスケすればいいやという思考はナンセンス。マルチタスク
メールの返信や電話対応をしているとメインの仕事が最速で終わらない。
メールの即レスよりもメインの仕事を速く終わらせることのほうが大事。
メインの仕事ができていれば、多少レスが遅くても何も言われない。質を重視する
仕事が終わる見通しが立っていないのに質を高めようとしてはいけない。勉強のための勉強
朝を無駄にする
速く仕事を終わらすためにやること
締め切りは絶対に守るものと考える
大前提。ロケットスタート時間術
計画の見積もりのために、スケジュールの2割の時間で8割の仕事を終わらせ、残りの8割の時間は流しで仕事をする。
2割の時間で8割終わらなければ、リスケの交渉をする。
2割の時間中は仕事を終わらすことだけに集中して、他の仕事は一切しない。
界王拳のようなアニメや漫画の全力状態をイメージするといい。崖から飛び降りながら飛行機を組み立てる
手を動かしながら考える。
実際にやってみないとわからないことも多いので、とにかくやってみることが大事。 スピードを重視すると、質が悪くなるという声もあるが、一度100%と思った仕事もどうせ後からやり直すことになるので、まずはタスクの全体像を把握するためにクオリティが低くても終わらすことを優先する。 最速でプロトタイプを作って、後から細かいところを修正すればいいと思ってやろう。タスクリストを寝る前に作成する
15分刻みにタスクを分けてリストを作成して、次の日はそのリストに沿って仕事を進めていく。
15分というのがポイントで、タスクを細かく分けることで進んでいる感を出せる。朝活
朝は誰にも仕事の邪魔をされないし、出社するまでという擬似的な締め切りも設定できるので、活用しない手はない。流しの時間に勉強する
徹夜は仕事がノッている時にする
規則を守ることよりも仕事を終らせることを優先する
【読書記録】伊藤洋一さんの「0秒で動け」の感想・まとめ
今回は、こちらの本を読んだので簡単にまとめていきたいと思います。
行動力のある人の特徴
そもそも行動するには以下の2つが必要。
志や熱意、やる気、自信といった「マインド」
行動するのに必要な知識や考え方といった「スキル」
行動力のある人は「思い立ったらすぐ行動」→「フィードバックから気づきを得る」のサイクルを高速に回しており、行動すればするほど経験値が増えて、マインドやスキルも強化され、より次の行動を起こしやすくなっている。
だがしかし、「思い立ったらすぐ行動」ってのが難しい。
早く行動するための方法をこれから説明していく。
0秒で動くためのサイクル
行動の早い人は以下のサイクルを高速に回している。
事実・データ → 仮説 → 結論と根拠のピラミッド → 結論 → 行動
行動するためにはどう動くかの結論が必要であり、行動の早い人はとりあえずの結論=頭出しの結論を出すのが早い。
また、ここでの結論は正解である必要はない。
つまり、間違っててもいいから結論を早く出してしまうのがよいということ。
どうやって早く結論を出すか?
3つ程度の仮説(根拠)から「結論と根拠のピラミッド」を利用して結論を出す。
複数の仮説(根拠)をピラミッドの底辺として、テッペンにくる結論を導き出す。
この時、仮説を見ながら自分に「だから何?」と言い聞かせながら考えるのがポイント。
どういう結論が出れば、みんなが幸せになれるかを考えるのも良き。
どうやって仮説を立てるか?
事実やデータを見て「〜だろう」と言えることを考える。
この時、事実やデータを集めすぎると判断がかえって鈍くなるので、ある程度のデータが集まればOK。
また、初めは仮説のクオリティが低いかもしれないが、数をこなしてハイクオリティの仮説を立てられるようにしていけばOK。
結局:仮説を立てる習慣を身につけよう
ここまで来ると結局、行動を早くするためには、仮説を早く立てる必要があることに気づく。
そして、行動の早い人は直感を多用して仮説を立てている。
直感というと不確実なもののように聞こえるかもしれないが、数多の経験を積んだ人の直感は無意識に過去の経験に基づいたものであるので信頼できる。
今取り組もうとしているタスクに直結する経験をしたことがあれば、直感で判断できるため、行動は早くなるかもしれないが、実際は初挑戦のタスクも多い。
だから、常日頃から直感を鍛えておく必要がある。
例えば、「最近は AI を利用する企業が増えている」というニュースを見たときに、「へ〜〜」と聞き流すのではなく、この事実から何が言えそうか考える。
また、「とある企業が〜な問題をこんな風に改善した」というのを見聞きしたときには、「自分であればどう改善したか?」と考える。
このように、普段から仮説を立てる練習を積んでおけば、いざ自分が行動するときにも早く動けるようになる。
みんな仮説を立てる練習していきましょ〜!
【読書記録】習慣化で超集中力を身につける
今回は、こちらの本を読んだので簡単にまとめていきたいと思います。
集中力をあげるには?
そもそも集中力の源は前頭葉にあるウィルパワーです。
しかも、その量には限界があり、徐々に消費されていきます。
だから、集中力を上げる方法は大きく分けて以下の2つ。
ウィルパワーの量そのものを増やす
ウィルパワーを節約する
今回は特に、2つ目のウィルパワーの節約についてまとめました。
習慣化でウィルパワーを節約
ウィルパワーを節約するには行動を習慣化します。
実際、アスリートのような集中力のある人は以下のサイクルが身についているそうです。
1つのこと集中して最速で身につける
集中力を使わず自動的にできるようになる
余った集中力で別のことを身につける
毎日、朝起きてから夜眠るまでの間に我々はたくさんの判断、選択をしており、その度にウィルパワーが消費されます。
しかし、行動を習慣化することで、今まで意識的にしかできなかったことが無意識に行えるようになるため、ウィルパワーが浪費されず、集中力も長続きするというわけです。
習慣化のポイント
一つは自分の中でルールを作ってしまうこと。
例えば、面倒になりがちなお風呂や洗濯を〜の後にするといった感じで決めておくだけでも、迷うことなく行動できるようになります。
また、一つずつ習慣化することも重要なポイントです。
一度にたくさんのことを習慣化しようとしても集中力が分散してしまい、結局どれも習慣化できないなんてことになってしまいます。
一つのことに集中して習慣化できたら、次のことを習慣化する。
これを積み重ねることで、無意識に行えることを一つずつ増やしていきましょう!
コピペするだけ!fishでnodebrewを使ってNode.jsをインストールする
はじめに
速攻で環境構築したい方向けです。
環境はmacOSでhomebrewは入っている前提で進めていきます。
homebrewが入っていない人はこの辺を参考にインストールしたり、使い方を学んだりしてください。
Node.jsをインストール
最新版のNode.jsをインストールするコマンド一覧です。
バージョンを指定したい場合は、latest
部分をnodebrew ls-remote
で表示される好きなバージョン(例えば、v12.3.1
)に変更してください。
$ brew install nodebrew $ nodebrew -v $ nodebrew ls-remote $ mkdir -p ~/.nodebrew/src $ nodebrew install-binary latest $ nodebrew ls $ nodebrew use latest $ set -U fish_user_paths $HOME/.nodebrew/current/bin $fish_user_paths $ node -v
簡単なコマンド説明
1行目〜2行目
Node.jsのバージョン管理のために、nodebrewをインストールします。3行目〜5行目
ls-remote
で表示されるNode.jsのバージョンを選び、install-binary <バージョン>
でインストールします。6行〜7行目
ls
でインストール済みのNode.jsを確認し、use
で使用するバージョンを決定します。8行目〜9行目
パスを通して、nodeコマンドが使えるようになればOK。
fishでのパスの通し方は以下が参考になります。
fish.rubikitch.com
Googleに学ぶコードレビューで確認すべきポイント
Googleがコードレビューのガイドラインを公開してくれました。
google.github.io
google.github.io
そのガイドラインを参考に、「コードレビューで確認すべきポイント」を簡単にまとめました。
コードレビューって具体的に何をすればいいか分からないって人はぜひ読んでみてください。
コードレビューとは?
そもそもコードレビューとは何か?
”コードの作者以外の人がそのコードを確認するプロセス”
何のためにするのか?
”コードとプロダクトの品質を維持するため”
コードレビューの原則
”完璧なコードはないが、より良いコードは存在する”
レビュアーは開発者に完璧さではなく、継続的な改善を求める行動を取る必要があります。
また、CL(changelistの略、GitHub等で見れるコードの変更点)が完璧でなくても、システム全体のコードの健全性を改善できることが分かれば、レビューを承認してOKです。
コードレビューで確認すべきポイント
ここから、レビュー時にどんな項目をチェックすればいいのかを見ていきます。
設計
最重要項目。
CLがシステムにとって適切な設計になっているか確認します。
具体的には、CL内のコード同士のもつ関係に意味があるか?、CLがシステムの他の部分とうまく統合できているか?などを検討します。
機能
CLが開発者の意図通りに動作するか確認します。
また、開発者の意図がエンドユーザおよび、将来そのコードを利用する人にとって適切かどうかも確認します。
多くの場合、開発者はレビュー依頼前にCLが正しく機能するかどうかをテストします。
そのため、レビュアーは以下のような点に特に注目するといいです。
- UIへの変更等、ユーザに影響が出る部分
- 並列プログラミングにおいてデッドロックや競合状態が起こらない安全な設計になっているか
複雑さ
コードが十分にシンプルか確認します。
もう少し具体的に言うと、コードを読む人にとって理解しやすいか、別の開発者がコードを利用したときにバグが起きにくいか等を見ます。
また、開発者が将来を見越して必要以上にコードを汎用的にしたり、不必要な機能を追加していないか(オーバーエンジニアリング)にも注意します。
テスト
CLに適した単体テストや結合テスト、E2Eテストがあり、それらが実際に有用であるか確認します。
テストのためのテストは用意せず、人が有用性を判断します。
コードに不備があったときに本当にテストは失敗するのか?有用なアサーションを出してくれるのか?等を見ます。
命名
変数やクラス、メソッドに明確な名前が付けられているか、名前を見ればその変数やメソッドが何を表しているか分かるか確認します。
コメント
必要十分なコメントがつけられており、内容が明確で分かりやすいか確認します。
コメントは複雑なアルゴリズムや正規表現につける場合を除き、コードの処理内容ではなく、コードの存在意義を説明するためにつけます。
コードの処理内容に説明が必要ならば、そのコードはもっとシンプルにできるはずです。
スタイル
コードが組織内のスタイルに則って書かれているか確認します。
また、一つのCLでスタイルの変更とその他の変更が混ざっていないかも確認します。
例えば、ある機能の追加とファイルのフォーマットという変更を1つのCLにしてしまうと、マージやロールバックの際に問題が発生しやすくなるため、CLを分ける必要があるのです。
ドキュメント
CLに関連するドキュメントにも必要な変更が加わっているか確認します。
例えば、新たなテストコードを作成したにも関わらず、その実行方法がドキュメントに記載されていない場合は開発者に追記を依頼します。
以上、簡単なまとめでした。
これを参考にどんどんレビューしていきましょう!
もっと詳しいことが知りたいって人は公式ガイドラインを見ましょう。
レビューを早くする方法やレビューコメントの書き方、CLの作り方なども説明されているので読む価値ありです。
エンジニアもビジネスを知るべき?〜新規事業でインターンしてみて〜
内定者インターンに2週間ほど行ってきました。
僕はエンジニアとして入社するんですが、インターンでは総合職?をしていました。
新たに得られた知見や学びを残しておこうと思います。
インターンの背景
なぜエンジニア志望の人間が2週間コードを書かず、総合職をしていたのか。
その理由は、
「インターンで最も価値の高い経験がしたかったから」
です。
エンジニアとして入社すれば、コードを書いたり技術的なことを学ぶ機会はいくらでもあるし、別のインターンでも少しは経験している。
一方、ビジネスサイドのことは全く知らない。般教で経済学を取ったことがあるぐらい(しかも結果はD)
ただ、プロデューサーやエンジニアが一体となって事業を成功させるには、エンジニアにもビジネスの知識が必要...?
ビジネスの知識があればエンジニア発信で面白いプロダクトが作れそう...?
こういった背景から、
「新規事業の総合職でのインターン」
が一番貴重な経験ができるかなと思って希望しました。
要望を通してくれた会社に感謝です。
業務内容
業務内容は主に以下の4つで、新規事業に関わる調査がメインって感じです。
- 競合調査
- マーケット調査
- 同一業界のIR情報の分析
- アタックリストの作成
得られた学び
進捗報告の重要性
一つ一つの作業の仕方云々というより、社会人としての働き方を知ることができたのが一番の学びでした。
これは誰にでも共通することだと思うのですが、
「細かく進捗報告する」
ってことを最後まで言われ続けました。
なぜ、これが重要か?
我々は、振られたタスクをこなすだけでなく、価値のあるアウトプットを出す必要があります。
そのためには、タスクの依頼者に随時進捗を報告して、方向性を見過っていないか、今後の進め方はこれでいいか等の確認をする必要があるのです。
インターン中、特に初めの1週間はこれが全然できておらず、自分で考えて進めてしまっていました。
その結果、アウトプットが求められているものとは違っており、「その資料は100円でも買わない」「それは調査結果ではなくただの感想文」ってな感じでズタボロに指摘を受けて、身をもって進捗報告の重要性を知りました。
エンジニアとの違い
何が正解か分からないって点でエンジニアとの違いがあると思いました。
賛否あるかもですが、エンジニアの場合、プログラムがテストに通ってかつ思ったように動作してくれれば最低限OKだと思います。
つまり、タスクの最終目的がはっきりしていることが多いと思うんです。
一方、インターン中に行った調査に正解とかはなくて、アウトプットを見る人が何を求めているか?どんな価値のある情報を提供できるか?を意識しないとタスクの目的がぼやけやすいなと感じました。
課題と今後
自覚していたことではあるけれど、ビジネスのこと知らなすぎ。
仕事中に出てくる用語ほぼ全部初見で、株価とかも何?って感じ。
普段政治・経済系のニュースは全く見ないし、見ても知識ないせいで1ミリも面白くないから、避けてきてたのですが、インターンを通してもっとビジネスのことを知ろう、知りたいと思うようになりました。
入社までに基本的な知識を身につけようと思います。
とりあえず、以下の2冊を読むところから始めます。
ちなみに、右は会社のすごい人におすすめしてもらいました。
Django標準の機能で実装する一番シンプルなユーザ認証
はじめに
Firebase等を使用せずに、Djangoにもともとあるライブラリのみでユーザー認証機能を作成します。
Djangoのプロジェクトがすでに立ち上がっており、テンプレートbase.html
とそれを拡張したindex.html
は最低限作られている前提で進めていきます。
ログイン・ログアウト
urlの設定
app/urls.py
に以下を記述します。
ログイン・ログアウトのViewはDjango側がdjango.contrib.auth
に用意してくれているので、それらをimport
して使います。
from django.urls import path from . import views from django.contrib.auth import views as auth_views app_name = "app" urlpatterns = [ path("", views.index, name="index"), path("login/", auth_views.LoginView.as_view(template_name="app/login.html"), name="login"), path("logout/", auth_views.LogoutView.as_view(), name="logout"), ]
settings.py
に以下を追記します。
<アプリ名>:<表示したいページ>
という形でログイン関連のURLをDjangoに教えてあげます。
LOGIN_URL = 'app:login' # ログイン時 LOGIN_REDIRECT_URL = 'app:index' # ログイン後 LOGOUT_REDIRECT_URL = 'app:index' # ログアウト後
ログインページの作成
login.html
を以下のように作成します。
{% extends 'app/base.html' %} {% block content %} <h2>ログイン</h2> <form method="POST">{% csrf_token %} {% if form.errors %} <p>ユーザー名またはパスワードが間違っています。もう一度入力してください。</p> {% endif %} <h2>ログイン</h2> <label>ユーザー名</label> <input name="username"> <br> <label>パスワード</label> <input type="password" name="password"> <br> <input type="submit" value="ログイン"> </form> {% endblock %}
動作確認
localhost:8000/login/
にアクセスして以下のようなシンプルなフォームが見れればOKです。
ログイン判定
ユーザーがログイン済みかどうかはUserオブジェクトのis_authenticated
フィールドの値によって判定できます。
使用例として、ユーザーがサイト訪問時にログイン済みであればログアウトボタンを、未ログインであればログインボタンとユーザー登録ボタンを表示するというのがあるでしょう。
{% if request.user.is_authenticated %} <a href="{% url 'app:logout' %}">ログアウト</a> {% else %} <a href="{% url 'app:login' %}">ログイン</a> <a href="{% url 'app:signup' %}">ユーザー登録</a> {% endif %}
ユーザー登録
urlの設定
app/urls.py
に以下を記述します。
from django.urls import path from . import views from django.contrib.auth import views as auth_views app_name = "app" urlpatterns = [ path("", views.index, name="index"), path("login/", auth_views.LoginView.as_view(template_name="app/login.html"), name="login"), path("logout/", auth_views.LogoutView.as_view(), name="logout"), path("signup/", views.signup, name="signup"), ]
Viewの作成
ユーザー登録のためのViewはあらかじめ用意されていないので自分で作ります。
app/views.py
に以下を記述してください。
ユーザー登録に必要なフォームはdjango.contrib.auth.forms
のUserCreationForm
を使います。
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth import authenticate, login def signup(request): if request.method == "POST": form = UserCreationForm(request.POST) if form.is_valid(): new_user = form.save() input_username = form.cleaned_data("username") input_password = form.cleaned_data("password1") # ユーザーを認証する new_user = authenticate(username=input_username, password=input_password) if new_user is not None: # ユーザーをログイン状態にする login(request, new_user) return redirect("app:index") else: form = UserCreationForm() return render(request, "app/signup.html", {"form": form})
ユーザー登録ページの作成
signup.html
を以下のように作成します。
{% extends "app/base.html" %} {% block content %} <h2>ユーザー登録</h2> <form method="POST" action="{% url 'app:signup' %}">{% csrf_token %} <label>ユーザー名</label> {{ form.username }} {{ form.username.errors }} <br> <label>パスワード</label> {{ form.password1 }} {{ form.password1.errors }} <br> <label>パスワード(確認)</label> {{ form.password2 }} {{ form.password2.errors }} <br> <input type="submit" value="登録する"> </form> {% endblock %}
動作確認
localhost:8000/signup/
にアクセスすると以下のようなシンプルなフォームが現れるはずです。
【VS Code】BlackとFlake8を使ってきれいなPythonコードを書く!!
はじめに
チーム開発をしていると、コードのフォーマットや書き方の癖が人によって違うことが多々あります。
その違いをなくすためのツールがリンタやフォーマッタです。
これらを導入することで、チーム内でのコーディングのルールを統一できるだけでなく、コードもきれいになります。
ここでは、最近話題のVS CodeにFlake8とBlackを導入する方法を説明します。
似たようなツールは他にもありますが、Flake8とBlackを組み合わせて使うのが個人的に気に入っています。
Flake8とBlack
Flake8
pythonにはpep8というコーディング規約があります。
Flake8はコードがこの規約通りに書けているか、シンタックスエラーがないかなどをチェックしてくれるツールです。
Flake8は以下のツールのラッパーです。
pyflakesは論理的なエラーを、pycodestyleはpep8に沿っているかを、mccabeは循環的複雑度をチェックします。
Black
Flake8はコードのチェックには非常に便利ですが、エラーの修正まではしてくれません。
このエラー修正を自動でやってくれるのがBlackです。
Blackはpep8に準拠しているので、pep8に関するエラーを自動で修正してくれるだけでなく、改行の仕方やクォーテーションの使い方まで統一してくれます。
また、Blackには設定項目がほとんどないため、導入がとても簡単です。
Flake8とBlackの詳細についてはドキュメントを見てください。
github.com
flake8.pycqa.org
BlackとFlake8のインストール
まず、Flake8とBlackを導入するPythonのプロジェクトを作成します。
ホストが汚れるのが嫌なので仮想環境を作ります。
環境さえ作れればここは何でも大丈夫です。
$ python3 -m venv venv $ source venv/bin/activate
環境ができたら、pip
でBlackとFlake8をインストールします。
$ pip install black flake8
pipenv
を使う場合は以下を参考にしてみてください。
www.m3tech.blog
qiita.com
VS Codeの設定
まず、「Python ms-python.python」という拡張機能をインストールして有効にします。
次に、ワークスペースの設定から右上の{}
を押して.vscode/settings.json
を開きます。
ここに、以下を加えます。
デフォルトのリンタのpylintはOFFにしておく必要があります。
また、Blackが定める一行あたりの文字数は88文字で、Flake8は79文字です。
この違いをなくすために、12行目でFlake8の最大文字数を88文字に変えています。
細かい設定等は、python.linting.flake8Args
とpython.formatting.blackArgs
で変えられるので、ドキュメントを見つつ、自分好みにしましょう。
{ // pythonの環境設定 "python.pythonPath": "venv/bin/python", "files.watcherExclude": { "**/venv/**": true }, // リンタの設定 "python.linting.pylintEnabled": false, "python.linting.flake8Enabled": true, "python.linting.lintOnSave": true, "python.linting.flake8Args": [ "--max-line-length", "88", "--ignore=E203,W503,W504" ], // フォーマッタの設定 "python.formatting.provider": "black", "editor.formatOnSave": true, "editor.formatOnPaste": false }
動作確認
以下のコードを例にして動作を確かめます。
def foo(): print( "Hello" "World" )
上のコードを保存した時に自動できれいなコードに変わればOKです。
def foo(): print("Hello" "World")
ちなみに今はエラーは出ないですが、Flake8のエラーは以下のような感じで出ます。
参考サイト
docker-compose + Django + postgreSQL + nginxで開発環境を構築
はじめに
初めにdocker-compose + django + postgreSQLのシンプルな環境を作り、その後docker-compose + django + postgreSQL + nginxでより実践に近い環境を作ろうと思います。
dockerやdocker-composeはすでにインストールされている前提で進めていきます。
環境はこんな感じ。
- macOS Mojave 10.14.5
- Docker: 18.06.1-ce-mac73
- Python: 3.7
- Django: 2.2.2
- PostgreSQL: 11
- psycopg: 2.8.3
- uWSGI: 2.0.18
開発環境の構築(docker-compose + django + postgreSQL)
dockerの公式ドキュメントを参考に進めていきます。
後のnginxの利用も考えて、最終的に以下のようなディレクトリ構成になるようにします。
. ├── docker-compose.yml ├── src │ ├── manage.py │ └── mysite │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── web ├── Dockerfile └── requirements.txt
Dockerfile
バージョンの指定以外は公式ドキュメントの通りです。
FROM python:3.7 ENV PYTHONUNBUFFERED 1 RUN mkdir /code WORKDIR /code COPY requirements.txt /code/ RUN pip install -r requirements.txt COPY . /code/
requirements.txt
pip
でインストールするものをここに記述します。
バージョンはこのブログの投稿時点での最新のものです。
Django==2.2.2 psycopg2==2.8.3
docker-compose.yml
PostgreSQLのバージョンは11に指定しています。
また、コンテナごとにディレクトリを分けているので、webコンテナのbuild
とvolumes
のパスが公式ドキュメントとは異なる点に注意してください。
version: '3' services: db: image: postgres:11 web: build: ./web command: python manage.py runserver 0.0.0.0:8000 volumes: - ./src:/code ports: - "8000:8000" depends_on: - db
プロジェクトの作成
以下のコマンドを使って、docker上でdjangoのプロジェクトを作成します。
run
コマンドは指定のサービス (今ならweb) のコンテナ内でコマンドを実行するものです。
$ docker-compose run web django-admin startproject mysite .
コマンドが正常に終了すると、ローカルにsrc/mysite/settings.py
が作成されるので、それを以下のように編集します。
これはdjangoがデータベースとしてPostgreSQLを利用するための設定です。
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'postgres', 'USER': 'postgres', 'PASSWORD': 'password', 'HOST': 'db', 'PORT': 5432, } }
動作確認
コンテナを立ち上げて、ブラウザでページが正しく表示されるか確認します。
docker-compose.yml
があるディレクトリで以下のコマンドを実行してください。
$ docker-compose up
ブラウザでlocalhost:8000
にアクセスして、以下の画面が表示されれば成功です。
スポンサーリンク
開発環境の構築(docker-compose + django + nginx)
続いて、docker-compose + django + nginxでの開発環境の構築方法についてです。
先ほど作成した環境にnginxの設定を追加する形で進めていきます。
最終的には以下のようなディレクトリ構成になります。
. ├── docker-compose.yml ├── nginx │ ├── conf │ │ └── mysite_nginx.conf │ └── uwsgi_params ├── src │ ├── manage.py │ ├── mysite │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-37.pyc │ │ │ ├── settings.cpython-37.pyc │ │ │ ├── urls.cpython-37.pyc │ │ │ └── wsgi.cpython-37.pyc │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ └── static └── web ├── Dockerfile └── requirements.txt
uWSGIの導入
nginxをサーバとして利用する場合、クライアントはDjangoではなくnginxにリクエストを投げることになります。
nginxはリクエスト内容を見て、レスポンスを返したり、djangoにリクエストを渡したりします。
しかし、nginxとdjangoは直接通信できないので、2つをつなぐuWSGIというWSGI (Web Server Gateway Interface) 必要です。
uWSGIはpip
でインストールできるので、requirements.txt
を以下のように記述します。
Django==2.2.2 psycopg2==2.8.3 uwsgi==2.0.18
uWSGIには設定ファイルであるuwsgi_params
が必要です。
以下をコピペして使ってください。
中身は気にする必要はありません。
uwsgi_param QUERY_STRING $query_string; uwsgi_param REQUEST_METHOD $request_method; uwsgi_param CONTENT_TYPE $content_type; uwsgi_param CONTENT_LENGTH $content_length; uwsgi_param REQUEST_URI $request_uri; uwsgi_param PATH_INFO $document_uri; uwsgi_param DOCUMENT_ROOT $document_root; uwsgi_param SERVER_PROTOCOL $server_protocol; uwsgi_param REQUEST_SCHEME $scheme; uwsgi_param HTTPS $https if_not_empty; uwsgi_param REMOTE_ADDR $remote_addr; uwsgi_param REMOTE_PORT $remote_port; uwsgi_param SERVER_PORT $server_port; uwsgi_param SERVER_NAME $server_name;
nginxコンテナの作成と設定
nginxのコンテナを以下のように定義します。
8000番ポートを開けて、設定ファイルや./static/
ディレクトリをマウントしています。
./static/
のマウントはcss等の静的ファイルをnginxから配信するためです。
nginx: image: nginx:alpine container_name: nginx ports: - "8000:8000" volumes: - ./nginx/conf:/etc/nginx/conf.d - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params - ./src/static:/static depends_on: - web
nginxのコンテナからdjangoのコンテナにリクエストを流すために、設定ファイルmysite_nginx.conf
を追加します。
ローカルの8000番ポートでリクエストを受けて、/static
なリクエストであればnginxがレスポンスを返し、それ以外はdjangoにリクエストを流すという様な設定です。
upstream django { ip_hash; server web:8001; } server { listen 8000; server_name 127.0.0.1; charset utf-8; client_max_body_size 75M; location /static { alias /static; } location / { uwsgi_pass django; include /etc/nginx/uwsgi_params; } }
nginxおよびuWSGIの設定に関しては、以下のサイトが分かりやすいです。 uwsgi-docs.readthedocs.io www.mathpython.com www.python.ambitious-engineer.com
その他のコンテナの設定
nginxコンテナ追加後のdocker-compose.ymlは以下のようになります。
説明はコメントで入れています。
version: '3' # データベースのデータを永続化する # 作成されたvolumeは docker volume ls で確認できる volumes: dbdata: services: nginx: image: nginx:alpine container_name: nginx ports: - "8000:8000" volumes: - ./nginx/conf:/etc/nginx/conf.d - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params - ./src/static:/static depends_on: - web db: image: postgres:11 container_name: db ports: - "5432:5432" # トップレベルで指定したvolumeをマウントする volumes: - "dbdata:/var/lib/postgresql/data" environment: POSTGRES_PASSWORD: password # settings.pyで設定した値と同じにする web: build: ./web container_name: web # uwsgiを使ってアプリケーションを起動させる command: uwsgi --socket :8001 --module mysite.wsgi volumes: - ./src:/code - ./src/static:/static expose: - "8001" depends_on: - db
staticディレクトリの設定
ページにcssやjavascript等の静的ファイルを反映させるために、src/mysite/settings.py
に以下を追記して、コマンドを実行します。
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
$ docker-compose run web ./manage.py collectstatic
動作確認
コンテナを立ち上げて、ブラウザでlocalhost:8000
にアクセスしてページが正しく表示されるか確認します。
$ docker-compose up
以下の画面が表示されれば成功です。
余談(環境構築後のプロジェクトの諸設定)
言語とタイムゾーンの設定
日本語化とタイムゾーンを東京に設定するには、src/mysite/settings.py
を以下のように編集します。
LANGUAGE_CODE = 'ja-JP' TIME_ZONE = 'Asia/Tokyo'
アプリケーションの追加
以下のコマンドでアプリを作成し、src/mysite/settings.py
に作成したアプリを追記します。
$ docker-compose run web ./manage.py startapp <アプリ名>
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', '<アプリ名>', ]
その他の参考サイト
【C++】csvファイルへの出力とファイルの結合方法
小ネタ。
csvファイルをいちいち開いてコピーするのが面倒だったので、ターミナルでやる。
csvファイルへの出力方法
C++でcsvファイルに出力する方法は、通常のファイル出力と同じ。
#include <iostream> #include <fstream> using std::endl; using std::ofstream; int main() { ofstream ofs("test.csv"); // ファイルパスを指定する ofs << 1 << ", "<< 2 << ", " << endl; ofs << 3 << ", "<< 4 << ", " << endl; ofs << 5 << ", "<< 6 << ", " << endl; return 0; }
出力されたファイルを開くと、以下のような結果が得られる。
$ open test.csv
csvファイルの結合方法
csvファイルを結合するにはcat
コマンドかpaste
コマンドを使う。
cat
コマンドを使うと縦方向に結合できる。
$ cat test.csv test.csv > Result.csv
paste
コマンドを使うと横方向に結合できる。
$ paste -d , test.csv test.csv > Result.csv
参考サイト
Factory MethodパターンとAbstract Factoryパターンの2つの違い
はじめに
Factory MethodパターンとAbstract FactoryパターンはどちらもFactory系だけど具体的にどう違うのかなと思っていると、こちらのサイトに自分の知りたいことがほとんど書いてありました。
dzone.com
簡単に内容をまとめたものをメモとして残しておこうと思います。
結論を先に述べると、2つのパターンの最大の違いは生成するインスタンスの数です。
Factory MethodパターンとAbstract Factoryパターン
まずは2つのパターンについて簡単に説明しておきます。
- Factory Methodパターン
Template Methodパターンをインスタンス生成に応用したもので、スーパークラスでインスタンス生成するためのAPI (factory method) を定義しておき、サブクラスで具体的なインスタンス化の方法を各々実装します。
www.macky-studio.comwww.macky-studio.com
- Abstract Factoryパターン
Abstract Factoryクラスがあるインスタンス及び、そのインスタンスの生成に必要なインスタンス群を生成するためのAPIを提供し、サブクラスでそれらの具体的な実装を行います。
Factory MethodパターンとAbstract Factoryパターンの違い
2つのパターンの違いは大きく分けて2つあります。
それぞれ順番に説明していきます。
- 生成するインスタンスの数
Factory Methodパターンは一つのインスタンスを生成するためのものですが、Abstract Factoryパターンは複数の部品を組み合わせて一つのインスタンスを生成する時に使います。
- メソッドかクラスか
名前の通り、Factory Methodはメソッドで、Abstract Factoryはクラスで表現します。
Factory Methodは単なるメソッドに過ぎず、Factory Methodを持つクラスの役割はインスタンス生成だけとは限りません。
一方、Abstract Factoryクラスの役割は一連のインスタンスを生成することです。
また、メソッドかクラスかに伴って抽象度も異なります。
Factory Methodパターンはメソッドレベルの抽象化ですが、Abstract Factoryパターンはクラスレベルの抽象化なので、後者の方がより抽象的です。
おわりに
参考サイトには理解できない部分もあったので、自分のわかる範囲でまとめましたが、以上がFactory MethodパターンとAbstract Factoryパターンの違いです。
生成するインスタンス・部品の数や抽象化のレベルを意識するといい感じだと思います。
SwiftUIのチュートリアルの文法(コンピューテッドプロパティ)について
はじめに
先日、SwiftUIが発表されて最近ちょくちょく公式チュートリアルをやっています。
2つ目のチュートリアルをやっていると、見たことのないプロパティの定義方法が出てきたので、今回はそれについてまとめてみました。
これが問題のコードです。
var locationCoordinate: CLLocationCoordinate2D { CLLocationCoordinate2D(latitude: coordinates.latitude, longitude: coordinates.longtitude) }
ぱっと見コンピューテッドプロパティっぽいですが、gettterやsetterやreturn文がありませんよね。
この問題を解決すべく、まずコンピューテッドプロパティについて簡単に説明し、その後上記のコードについて解説しようと思います。
コンピューテッドプロパティとは?
プロパティ自身が値を保持しておらず、プロパティへのアクセス時に計算した値を返すプロパティです。
ちなみに、値を保持するプロパティはストアドプロパティといいます。
プロパティの種類については以下の記事がわかりやすいです。
qiita.com
コンピューテッドプロパティは次のように使います。
import UIKit struct Circle { // ストアドプロパティ var radius: Double = 0 // コンピューテッドプロパティ var area: Double { get { return radius * radius * 3 } set { // プロパティに代入した値はnewValueでアクセス可能 radius = sqrt(newValue / 3) } } } var circle = Circle() circle.radius // 0 circle.area // 0 (getter) circle.radius = 5 circle.radius // 5 circle.area // 75 (getter) circle.area = 27 // (setter) circle.radius // 3 circle.area // 27
コンピューテッドプロパティを定義するときはget{}
でgetterを、set{}
でsetterを指定し、getterには値を返す処理を、setterには値を更新する処理を書きます。
上のコードでは、getterで半径に応じた円の面積を返す処理を、setterで円の半径を更新する処理を書いています。
ここで、コンピューテッドプロパティのgetterは必須ですが、setterは省略可能です。
import UIKit struct Circle { var radius: Double = 0 var area: Double { get{ return radius * radius * 3 } } } var circle = Circle() circle.radius // 0 circle.area // 0 (getter) circle.radius = 5 circle.radius // 5 circle.area // 75 (getter) circle.area = 27 // setterを定義していないのでコンパイルエラーになる
また、setterを省略した場合はget{}
を省略してgetterを書くこともできます。
import UIKit struct Circle { var radius: Double = 0 var area: Double { // getがなくてもgetterとして動作する return radius * radius * 3 } }
以上がこれまでのコンピューテッドプロパティの定義方法です。
スポンサーリンク
新しいコンピューテッドプロパティ?
コンピューテッドプロパティについて理解できたところで、初めのコードを見てみましょう。
var locationCoordinate: CLLocationCoordinate2D { CLLocationCoordinate2D(latitude: coordinates.latitude, longitude: coordinates.longtitude) }
これは、先程説明したsetterとget{}
を省略した形に似ていますが、return
までもが省略されています。
あれ?こんな書き方OKなの?といろいろ調べていると、以下のページが見つかりました。
ドキュメントのShorthand Getter Declarationでは次のように書かれています。
If the entire body of a getter is a single expression, the getter implicitly returns that expression.
つまり、getterの中身の式が一つだけなら、return
は書く必要がないってことです。
この暗黙的なreturn
は、短いコードのreturn
の占める割合多くね?単一式だったらreturn
書いてるってことにしようよってことで採用されたようです。
そのため、暗黙的なreturn
は関数の定義においても使えます。
func oldCircleArea(with radius: Int) -> Int { return radius * radius * 3 } func newCircleArea(with radius: Int) -> Int { radius * radius * 3 // returnを省略 } print(oldCircleArea(with: 5)) // 75 print(newCircleArea(with: 5)) // 75
まとめ
Swift5.1から関数の定義やgetterでは単一式の場合にreturn
が省略でき、コード量を減らせるようになります。
SwiftUIのチュートリアルはとてもわかりやすいのですが、文法の解説がないのがなんだかなーって感じです。
他にも気になった文法が出てくれば、調べてまとめようと思います。
BuilderパターンとFactory Method・Template Methodパターンの違いは?
Java言語で学ぶデザインパターン入門を読んでいて疑問に思う点があったが、いろいろググって多分理解したのでメモ。
今回の題材は、第7章で学ぶBuilderパターン。
解説するのは、タイトルにもある以下の2つの疑問について。
- Factory Methodパターンとどう違うのか?
- Template Methodを使えばいいのではないか?
Builderパターンとは?
複雑な構造を持ったインスタンスの作成を簡単にしてくれるパターン。
インスタンスを組み上げていく過程をブラックボックス化できる。
Builder役、ConcreteBuilder役、Director役、Client役があり、Builderでインスタンスの各部分を作るためのメソッドを定義し、Directorがそれらのメソッドを使ってインスタンスを作成する。
※Builderパターンの細かい説明等は書籍や参考サイトを参照してください。
<参考サイト>
7. Builder パターン | TECHSCORE(テックスコア)
Javaで書くBuilderパターンのパターン - Qiita
Builderパターン - Qiita
Tech Tips: デザインパターン(7) Builder
Factory Methodパターンとどう違うのか?
一つ目の疑問。 Factory MethodパターンもBuilderパターンもどちらもインスタンス作成のためのパターンであるが、具体的にどう違っていて、どう使い分ければいいのだろうか?
Factory MethodパターンとBuilderパターンの違いを理解するポイントは、
「インスタンス作成時のどの処理を共通化しているか」
であるように思う。
それぞれのパターンで例を出しつつ見ていこう。
Factory Methodパターンにおける共通化
Factory Methodパターンは、全くタイプの異なるオブジェクトの作成処理を共通化してフレームワークとして提供する。
例として、本とカードを製造している一つの工場を考える。
この工場では本とカードは以下の手順を踏んで出荷される。
- 本・カードを作成する
- 完成した本とカードを商品登録する
- 出荷する
ここで重要なのが、本・カード自体の作成過程は当然異なるが、ステップ1〜ステップ3の流れは共通しているということ。
そのため、ステップ1〜ステップ3のプロセスをインスタンス作成のためのフレームワークとして提供できる。
Builderパターンにおける共通化
Builderパターンは、似たオブジェクトの作成過程を共通化して、手順をきれいにまとめる。
例えば、ベンツとBMWの車を作る工場を考える。
Factory Methodパターンの例と違って、この場合は両者とも車なので製造過程は同じで、材料やデザインが異なるだけである。
つまり、Builderパターンは、Factory Methodパターンに比べてミクロな視点で共通化しており、インスタンスそのものの作成過程をまとめるためのものである。
Template Methodを使えばいいのではないか?
二つ目の疑問。
Builderパターンは以下のようなクラス図(Wikipediaより引用)であるが、なぜわざわざDirectorを用意する必要があるだろうか?
construct()
はTemplate MethodとしてBuilderの中に記述すればいいのではないか?
これの明確な答えはよく分からないが、「継承か委譲か問題」と関係が深いように思う。
Template Methodだとインスタンス生成はスーパークラスが担い(継承)、BuilderパターンだとDirectorが担う(委譲)。
どちらでも良さそうだが、委譲の方がクラス同士が疎結合で抽象度が高く良いらしいし、GoFのデザインパターンでも継承よりも委譲が推奨されている。
なので、とりあえずBuilderパターンは委譲を使うものなんだと覚えておこう。
<参考サイト>
Template Methodパターン
Differences between builder pattern and template method (builder vs template) - Stack Overflow
Prototypeパターンのメリットや使いどころは?
Java言語で学ぶデザインパターン入門を読んでいて詰まった点があったが、いろいろ調べたら腑に落ちたのでメモ。
何が分からなかったのかというと、第6章で登場するPrototypeパターンの使い所についてである。
Prototypeパターン自体は説明やサンプルコードを読めば理解できるのだが、このパターンが実際どういう場面で使われるのかがさっぱりだった。
一応Prototypeパターンについて説明しておくと、プロトタイプとして生成しておいたインスタンスをコピーして新たなインスタンスを生成するデザインパターンである。
Prototypeパターンの使い所
書籍の中では、Prototypeパターンの使い所として以下の3つが挙げられている。
- 種類が多すぎて、クラスにまとめられない場合
- クラスからのインスタンス生成が難しい場合
- フレームワークと生成するインスタンスを分けたい場合
それぞれどういうことを言っているのか順に見ていく。
以下のサイトの説明が分かりやすかった。
6. Prototype パターン | TECHSCORE(テックスコア)
Prototype
種類が多すぎて、クラスにまとめられない場合
まず一つ目。
これが一番の謎だった。
teratailとかの質問にもあったように、
「Prototypeパターンはあるクラスのインスタンスをコピーするだけだから、 似ているが異なるクラスを生成する代わりになんてなれんの?」
「若干のフィールドの違いであればコンストラクタの引数で対応できるくね?」と思った。
しかし、これは処理の重さを考えると、比較的容易に理解できた。
Prototypeパターンを使わなければ、フィールドの値が少し違うだけのクラスがたくさん作られてしまう。
または、1個のクラスにまとめて、コンストラクタの引数にフィールドの値を渡すかだ。
どちらにおいてもフィールドの値が少し違うだけのインスタンスを新たに生成する場合は、コンストラクタを使ってnewする必要がある。
これは、コンストラクタ内の処理が軽ければ、それほど問題がないように思える。
しかし、処理が非常に重く時間がかかる場合に、必要な種類分すべてnewしてインスタンスを生成するのは面倒である。
そういう時に、プロトタイプとなるインスタンスを登録しておけば、コピーしていろんなインスタンスを作れるし、クラス数も減らせるので便利だなと思った。
クラスからのインスタンス生成が難しい場合
これは比較的イメージしやすい。
グラフィックや図形をコピーしたいときに使う。
例えば、ゲームに複数個の同じ爆弾を登場させる時や、パワポの図形をコピーするときなどに使われているみたい。
ユーザのマウス操作を通して作成された複雑な図形やグラフィックをプログラム上でコンストラクタを使って再現するのは面倒そうだし、重たい処理をしなければいけないのが想像できる。
そこでPrototypeパターンを使うと、2回目以降のインスタンス生成が非常に低コストで行える。
フレームワークと生成するインスタンスを分けたい場合
これは使い所というかメリットに近い。
また、Factory Methodパターンと被るところも多い。
Prototypeパターンのクラス図は以下のようになる。(Wikipediaより引用)
Prototypeパターンではコピー対象のクラスは必ず、Prototypeになるためのインタフェースを実装しているので、Client側ではインスタンスをコピーする際に具体的なクラス名について知っておく必要がない。
つまり、フレームワーク側が具体的なクラス名に束縛されないということである。
余談:浅いコピーと深いコピー
Prototypeパターンで重要になるのが、コピーが深いかどうかであるが、浅いコピーと深いコピーに関してはこちらが分かりやすかった。
単にclone()するとプリミティブ型のデータは実体がコピーされるが、参照型のデータは実体への参照がコピーされるだけで、浅いコピーとなってしまう。
コピー元のフィードの型に気をつけなれけばならない。