akiyoko blog

akiyoko の IT技術系ブログです

「EducationTech Talks Tokyo #1」に参加してきました

会場

NHN テコラス株式会社
東京都新宿区新宿6-27-30 新宿イーストサイドスクエア EAST 13階


Twitter

togetter.com



 

全体の感想など

今回は EdTech 系のイベントでしたが、教育関係の勉強会は都内でも結構珍しいです。見かけたらなるべく参加するようにしていますが、それでも数ヶ月か半年に一回あるかないかでしょうか。。 特に今回のように、「足が長い」と言われている教育サービスの中でも急成長を遂げている三社のベンチャー企業の CTO が登壇してくれる機会もなかなかレアではなかったでしょうか。


全体的なテーマは「スケール」についてでした。突発的なスパイクや成長に合わせたインフラや技術的な対策など、エンジニアも少ない中いろいろと苦労されているようでした。


そういった教育関連サービスならではの事情やノウハウも非常に有益でしたが、個人的に技術的興味が湧いたのは、アオイゼミ の動画ストリーミング。ライブ配信は HLS / WebRTC とのことでしたが、遅延は 30秒くらいでしょうか。どのように遅延を少なくしているのか、いろいろとノウハウがありそうです。


あと、「PHPer」は「ぺちぱー」って読むんですね。初めて聞きました。




manabo CTO 山下氏


  • mana.bo アプリ
  • 高単価のサービスなのでスケールの規模感は小さい?
    • 1時間プランで 3,500円/月
  • 講師数7万人の株式会社トモノカイと提携、Z会グループと資本業務提携(有料ユーザの送客?)
  • スパイクが季節要因とキャンペーン要因
    • 可能な限りクラウドサービスを利用
  • イケてるサービスは TechCrunch(英語版)で発掘
    • CrunchBase(Wikipediaライクなスタートアップデータベース)
    • StackShare(サービスやツールがどのような技術で開発されているかをチェックできる)
    • デモデイ(Demo Day)
  • データ分析は、re:dashFirebase Analytics
  • 全エンジニアにデプロイ権限!


 

スタディプラス CTO 斉藤氏

  • 「学ぶ喜びをすべての人へ」
  • 従業員数 22名(エンジニア 9名、うちサーバサイド 3名?)
  • Studyplus(BtoB)
    • 勉強記録を可視化
    • 2016年9月現在で会員200万人以上(受験する高校3年生の3人に1人が利用)
    • 日本e-Learning大賞、2016 ベスト自己改善アプリ
  • Studyplus for School(BtoC)
    • 勉強状況をダッシュボードで見れるように
  • AWS導入事例:スタディプラス株式会社
    • 小難しい?属人化? ⇒ 誰でも構築できるシンプルな設計に!
  • サーバ側は Ruby on Rails
    • 特に、FatModel 問題、秘伝のタレ問題、クエリチューニングしずらい
  • RoR meets DDD でレイヤー分離
    • 責務がクリアになってコードの見通しがよくなった
    • ファイル数が増える。。



 

動画ストリーミングサービスのスケーリング事例

アオイゼミ CTO 青木氏


  • 中高生向けオンライン学習塾
    • リアルタイムの動画を観ながら、双方向のコミュニケーションが可能
    • 5,000円/月?
  • ライブストリーミング
    • オフィスにオンプレのサーバがある
    • (EC2で?)エンコードして CDN(CloudFront & Akamai の二段構え)で配信
    • CloudFormation Template で一式構築可能
  • ライブストリーミングが始まると急なバースト
    • APIアクセスの遅延対応として、Amazon Aurora 導入!
      • Aurora の Reader Endpoint を利用。オススメ
  • ログ保存は Kinesis を使ってストリームデータを保存
  • Firebase Analytics + BigQuery でデータ分析
    • re:dash も使ってる?
  • エンジニア5名?

今年の流行語大賞は「JPAP」(Jupyter / Python / Anaconda / Pyenv)〜 ゼロからはじめる Jupyter Notebook 〜

この投稿は 「jupyter notebook Advent Calendar 2016 - Qiita」 の 6日目の記事です。



今年の流行語大賞が 「JPAP(Jupyter / Python / Anaconda / Pyenv) に決まりましたね!



あれ? 違うの!?



といった冗談はさておき、ちょっとした統計解析をしようとしたときに、Jupyter Notebook(ジュピター ノートブック)+ Pandas(パンダス)を試してみたら、すごくいい感じだったのでご紹介。



Jupyter Notebook はブラウザ上でデータ分析関連の Python コードを実行したり実行結果を保存したりすることができる非常に便利なツールなのですが、Jupyter Notebook の他にも Python, NumPy, Pandas, Matplotlib 等々の環境を準備しておく必要があります。それをゼロから用意するのは大変だなぁと思うかもしれませんが、「Anaconda」というデータサイエンティスト向けのプラットフォームをインストールするだけで、Python 本体を含めたデータ分析関係の Python パッケージ群をいい感じに環境設定することができます。 *1

また Anaconda は、「Pyenv」という Python のバージョン管理ツールを使うことで、Mac や Linux などの環境に適当な Anaconda を簡単にインストールをすることができます。

f:id:akiyoko:20161206000608p:plain



ということで今回は、「JPAP(Jupyter / Python / Anaconda / Pyenv)」(Jupyter はじめるなら、データ分析関連の Python パッケージが全部入りの Anaconda を Pyenv でインストール!)と題して、Jupyter Notebook のセットアップ、および使ってみた感想を書いてみます。




まずは、Jupyter Notebook や Pandas の概要から。


 

Jupyter Notebook

Jupyter Notebook は、ブラウザ上でデータ分析関連の Python コードを実行したり実行結果を保存したりすることができる非常に便利なツールです。サーバを裏で起動しておくことで、ブラウザ上のいろいろな操作を受け付けることができます。


(参考)
A gallery of interesting IPython Notebooks · ipython/ipython Wiki · GitHub


ちなみに、Jupyter Notebook は少し前まで「IPython Notebook」と呼ばれていました。公式としては、2015年7月末から名前が変更になったということでしょうかね。

Jupyter Notebook was born (officially on July 30)


Jupyter Notebook - LIMSWiki



以下、Jupyter Notebook を使ってみた感想です。

Jupyter Notebook の良いところ
  • Anaconda さえあれば他にセットアップ不要
  • 画面でポチポチしてデバッグできる *2
  • 実行結果を含めて ipynb ファイルに保存できる *3
  • ipynb ファイルを GitHub にアップすれば誰でも実行結果を見れる

f:id:akiyoko:20161105114420p:plain
(例:bgstat/analytics_by_experience.ipynb at master · akiyoko/bgstat · GitHub

少し足りないところ
  • 変数の中身が全て出力されないところ(長いと「...」と省略されてしまう)
  • GitHub の画面でポチポチできない


 

Pandas

Pandas は、非常に便利に使える Python のデータ分析ライブラリです。DataFrame や read_excel、read_csv など、ありがたい機能が盛り沢山です。

pandas is an open source, BSD-licensed library providing high-performance, easy-to-use data structures and data analysis tools for the Python programming language.


http://pandas.pydata.org/

 

参考本

Pandas をガッツリ勉強するならこちら。



Pandas を手っ取り早く習得するなら、こちらの記事が一番かもしれません。
openbook4.me





 

JPAP(Jupyter / Python / Anaconda / Pyenv)の手順

Anaconda を Pyenv でインストールします。

以下、Mac 上でのインストール手順になります。


<過去記事>
akiyoko.hatenablog.jp

 

1.Pyenv のインストール

Homebrew で pyenv をインストールします。

$ brew install pyenv

$ pyenv --version
pyenv 20160303

$ cat << EOF >> ~/.bash_profile
export PYENV_ROOT=\${HOME}/.pyenv
export PATH=\${PYENV_ROOT}/bin:\$PATH
eval "\$(pyenv init -)"
EOF

$ source ~/.bash_profile

 

2.Anaconda のインストール

Anaconda 2 は Python 2.7系、Anaconda 3 は Python 3.5系のパッケージを同梱しています。 *4


私は(業務で使っているのが)Python 2系なので、最新の Anaconda 2 をインストールすることにしました。

ちなみに anaconda2-2.4.0 以降、 Anaconda 2 系のプレフィックスが「anaconda2-*」に統一されたようです。 *5

$ pyenv install -l
Available versions:
  2.1.3
  ・
  ・
  anaconda-2.4.0
  anaconda2-2.4.0
  anaconda2-2.4.1
  anaconda2-2.5.0
  anaconda3-2.0.0
  ・
  ・


Anaconda 2系で(現時点で)最新の「anaconda2-2.5.0」をインストールしました。

$ pyenv install anaconda2-2.5.0
$ pyenv global anaconda2-2.5.0
$ pyenv rehash

### バージョン確認
$ pyenv versions
  system
  anaconda-2.4.0
* anaconda2-2.5.0 (set by /Users/akiyoko/.pyenv/version)

$ python --version
Python 2.7.11 :: Anaconda 2.5.0 (x86_64)

$ pip list | grep -E "jupyter|notebook"
jupyter (1.0.0)
jupyter-client (4.1.1)
jupyter-console (4.1.0)
jupyter-core (4.0.6)
notebook (4.1.0)


ここで一瞬ハマってしまいました。。
conda update を実行して最新化しようとしたら、Python が 2.7.12 にアップグレードされてしまい、Jupyter Notebook 利用時に新規ファイルを作成しようとすると、「Python[conda root]」と「Python[default]」の 2バージョンが表示されてしまいました。

python 2.7.12 installed from anaconda and the default one came with Mac is 2.7.10.


http://stackoverflow.com/a/40051941

が原因かもしれません。
結局、conda update でアップデートするのはやめました。



 

3.Jupyter Notebook の起動方法

コマンドラインから以下を実行すれば OK です。

$ jupyter notebook


サーバ起動後にブラウザで「http://localhost:8888/」にアクセスすると、サーバを起動したディレクトリが階層表示されます。

f:id:akiyoko:20161105104659p:plain

あとはポチポチやるだけ。

f:id:akiyoko:20161105104738p:plain



 

実戦

ちょっとしたデータ解析をしてみました。

こんな感じで、D列から右方向にデータ列が並んでいる Excel シートがあるとします。

f:id:akiyoko:20161104235820p:plain


まずは、Pandas の read_excel() を使って Excel を読み込みます。
少し前処理をした後、DataFrame を加工して、偏差値などの代表値を加えたり、欠損値を除外したり、データをソートしたり、各代表値を計算したりします。

最後に、経験年数ごとにグループ化してグラフを描きました。

github.com




import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
%matplotlib inline

# header 無しで Excel を読み込む
excel_df = pd.read_excel('5th_backgammon_proficiency_test_score.xls', sheetname='Result', header=None)

「%matplotlib inline」は実行結果の画像をインラインで埋め込むためのおまじない。

# D列以降のデータのみを使用
df = excel_df.copy()
df = df.ix[:, 3:]

pandas.DataFrame.ix を使うと、DataFrame のスライスが簡単にできます。

# index と column を反転
df = df.T

今回試した Excel データが右方向にデータ列が並んでいるため、少しトリッキーなことをしました。

# column名を書き換え
columns = {
    0: 'Date',
    3: 'Nation',
    4: 'Sex',
    7: 'Experience',
    61: 'Total Score',
    64: 'Total Error',
    118: 'Early Game Score',
    119: 'Middle Game Score',
    120: 'End Game Score',
    121: 'Other Score',
    132: 'Early Game Error',
    133: 'Middle Game Error',
    134: 'End Game Error',
    135: 'Other Error',
}
df.rename(columns=columns, inplace=True)
# column の絞り込み
df = df[columns.values()]
# Date列が NaN のデータは除外
df = df.dropna(subset=['Date'])

pandas.DataFrame.dropna() で欠損データを除外することができます。

def get_deviation(s):
    """
    s: Series
    """
    return (s - s.mean()) / s.std(ddof=False) * 10 + 50

# 偏差値列を追加
df['Deviation of Total Score'] = get_deviation(df['Total Score'])
df['Deviation of Early Game Score'] = get_deviation(df['Early Game Score'])
df['Deviation of Middle Game Score'] = get_deviation(df['Middle Game Score'])
df['Deviation of End Game Score'] = get_deviation(df['End Game Score'])
df['Deviation of Other Score'] = get_deviation(df['Other Score'])
df['Deviation of Total Error'] = 100 - get_deviation(df['Total Error'])
df['Deviation of Early Game Error'] = 100 - get_deviation(df['Early Game Error'])
df['Deviation of Middle Game Error'] = 100 - get_deviation(df['Middle Game Error'])
df['Deviation of End Game Error'] = 100 - get_deviation(df['End Game Error'])
df['Deviation of Other Error'] = 100 - get_deviation(df['Other Error'])
# Total Score列の降順、Total Error列の昇順でソート
df = df.sort_values(by=['Total Score', 'Total Error'], ascending=[False, True])
df

pandas.DataFrame.sort_values() でソートができます。

# 各代表値を求める
sliced = df[['Total Score', 'Early Game Score', 'Middle Game Score', 'End Game Score', 'Other Score', 'Total Error', 'Early Game Error', 'Middle Game Error', 'End Game Error', 'Other Error']]
desc = pd.DataFrame(
    [sliced.max(), sliced.min(), sliced.mean(), sliced.median(), sliced.var(ddof=False), sliced.std(ddof=False)],
    index=['max', 'min', 'mean', 'median', 'var', 'std']
)
desc
# その他の統計量
df.describe()
# 前処理ここまで

# 経験年数ごとにグループ化
grouped = df.groupby('Experience')
# 出力用のデータフレームを作成
experience_df = pd.DataFrame()
experience_df['Size'] = grouped.size()
experience_df['Total Score'] = grouped['Total Score'].apply(np.mean)
experience_df['Early Game Score'] = grouped['Early Game Score'].apply(np.mean)
experience_df['Middle Game Score'] = grouped['Middle Game Score'].apply(np.mean)
experience_df['End Game Score'] = grouped['End Game Score'].apply(np.mean)
experience_df['Other Score'] = grouped['Other Score'].apply(np.mean)
experience_df['Total Error'] = grouped['Total Error'].apply(np.mean)
experience_df['Early Game Error'] = grouped['Early Game Error'].apply(np.mean)
experience_df['Middle Game Error'] = grouped['Middle Game Error'].apply(np.mean)
experience_df['End Game Error'] = grouped['End Game Error'].apply(np.mean)
experience_df['Other Error'] = grouped['Other Error'].apply(np.mean)
# indexの並び替え
experience_df = experience_df.reindex(['under 1 year', '1-2 years', '2-3 years', '3-4 years', '4-5 years', '5-10 years', 'over 10 years'])
experience_df

pandas.DataFrame.reindex() で index の並び替えができます。

# スコアのグラフ描画
experience_df.plot.bar(
    y=['Early Game Score', 'Middle Game Score', 'End Game Score', 'Other Score'],
    figsize=(10, 8),
    stacked=True,
    cmap='Blues')
plt.title(u'Score by Experience', size=16)
plt.plot()

pandas.DataFrame.plot.bar で棒グラフを描画することができます。

# エラーのグラフ描画
experience_df.plot.bar(
    y=['Early Game Error', 'Middle Game Error', 'End Game Error', 'Other Error'],
    figsize=(10, 8),
    stacked=True,
    cmap='Blues')
plt.title(u'Error by Experience', size=16)
plt.legend(loc='upper right')
plt.plot()


 

まとめ

冒頭でも書きましたが、ちょっとした統計解析をしようとしたときに Jupyter Notebook + Pandas を試してみたら、すごくいい感じでした。

Jupyter Notebook を使うための環境は、Anaconda をインストールすればゼロからでも一発で準備することができるので、非常に楽チンです。

Anaconda には、Python 本体や Jupyter Notebook のほか、Numpy や Pandas、Matplotlib までデータ分析に必要なライブラリが一通り揃っているので、あとはブラウザを使ってトライ&エラーでデータ分析を進めていくことができます。気軽にデータ分析を試すことができ、ipynb ファイルで実行結果を共有したりすることもできるので、非常に面白いツールだと感じました。



明日は、7of9 さんの 7日目の記事「Jupyter Notebook > NewボタンでPython2が選択できない時の対処方法 / Jupyterの気に入っているところ - Qiita」です。
よろしくお願いします。



 

参考本

「IPython」とタイトルに付いていますが、比較的新しい本です。



「IPython Notebook」は 2015年7月以降に「Jupyter Notebook」に改名されましたが、「IPython」自体は消滅したわけではなく、インタラクティブ Python シェルや Jupyter の Python 用のカーネルとして存在しています。

  • A powerful interactive Python shell
  • A Jupyter kernel to work with Python code in Jupyter notebooks and other interactive frontends.


IPython Documentation — IPython 6.2.1 documentation

*1:Anaconda に含まれるパッケージの一覧は、「Anaconda package lists | Anaconda: Documentation」から確認することができます。

*2:jupyter notebook コマンドでサーバを立ち上げる必要があります

*3:出力したグラフ(画像)もインライン表示で含めることができます

*4:Frequently asked questions | Anaconda: Documentation より

*5:AnacondaでPythonの分析環境をまとめてインストール - TASK NOTES より

ベスト・オブ・Django本!

この投稿は 「Django Advent Calendar 2016 - Qiita」 の 5日目の記事です。


「Django」を勉強するときに一番困るのは、やっぱり 「Django本がない」 問題ですよね?


実際、Django 初心者や初級者のエンジニアがいざ本格的に勉強しようと思っても、Django の専門書が圧倒的に少ないという問題が大きな壁として立ちはだかっているのではないでしょうか。


あっても古いとか。

開発のプロが教える標準Django完全解説―Webアプリケーションフレームワーク (デベロッパー・ツール・シリーズ)

開発のプロが教える標準Django完全解説―Webアプリケーションフレームワーク (デベロッパー・ツール・シリーズ)

2008年発売って、Django のバージョンは 1.0 ですよね。
さすがに情報が古すぎます。。


あと、Django に割かれているページ数が少ないとか。

Pythonプロフェッショナルプログラミング第2版

Pythonプロフェッショナルプログラミング第2版

Django について書かれた章がありますが、それでも 20ページ程度しかありません。。


「もっと新しくて、ちゃんと全体を網羅している Django の専門書はないの??」
私も常々、こんな疑問を抱いていました。



書籍の充実した Ruby や PHP と比べて、Python のフルスタック Webアプリケーションフレームワークに関する書籍が少ないという問題が、Django があまり流行ってない大きな原因になっているのでは??というのが、私の率直な印象です。




で、見つけましたー!!


私が自信を持ってオススメできる ベスト・オブ・ Django本が「Two Scoops of Django: Best Practices for Django 1.8」 です!

Two Scoops of Django: Best Practices for Django 1.8

Two Scoops of Django: Best Practices for Django 1.8


残念ながら全編英語の本ですが(*1)、設計のベストプラクティスから細かな Tips まで詳細に分かりやすく解説されている Django の専門書です。


対象者は、初心者というよりも、チュートリアル を読み終えたくらいの初級者から、現場で Django を何年か使っている中級者くらいを想定していると思われます。
専門書と言っても堅苦しさは全く無く、頻繁にアイスクリームショップの話が引き合いに出されたりと、ポップな世界観(!)を醸し出しています。


著者は Daniel Roy GreenfeldAudrey Roy Greenfeld の二人のエンジニアで(*2)、プロジェクトで Django を使い倒している超ベテランです。これまで、Django 1.5, 1.6 向けの Two Scoops of Djangoシリーズ を出版してきた経歴もあるので、本の内容の充実度ぶりは半端ないです。


本のタイトル「Two Scoops of Django(Django の 2つのスクープ)」の由来は、「二人がお届けする Django の最新情報(スクープ)」という意味と、Django をアイスクリームに見立てて「二すくい(スクープ)のアイスクリーム」という意味を掛けているんじゃないかと思われます。




ちなみに、現時点の Django の最新バージョンは 1.10 ですが、この本の対象バージョンは Django 1.8 です。しかしながら、1.8 は LTS(長期サポートバージョン)なので、少なくとも 2018年4月までは本家からセキュリティとバグフィックスが提供されることになるため、現場で使う場合も安心です。

f:id:akiyoko:20161122010842p:plain
Download Django | Django より引用)


著者は、次の「Two Scoops of Django」シリーズはバージョン 2.2 のリリース以降と言っているので、2019年まではこの「Two Scoops of Django: Best Practices for Django 1.8」にお世話になりそうです。なので、この本を買って 2019年まで読み倒すのがよいかと思います。


(2017/9/14 追記)

次の「Two Scoops of Django」シリーズはバージョン 2.2 のリリース以降、と言っていましたが、どういう心境の変化かバージョン 1.11 に対応した最新刊がつい先日発売になりました。バージョン 1.11 を導入する方にとっては朗報ですね!!

Two Scoops of Django 1.11: Best Practices for the Django Web Framework

Two Scoops of Django 1.11: Best Practices for the Django Web Framework


(2018/4/20 追記)

技術書典4」というイベントで、人生初の技術系同人誌「現場で使える 基礎 Django」を頒布することになりました。
動機はここにも書きましたが、「Django の日本語の書籍が少ない」と常々感じていたからです。「無いのなら 自分で書こう Django本」を実践してみたわけです。Django の日本語書籍が無くて困っている方、Django で一度挫折したことのある方に絶対オススメです。
あなたの「ベスト・オブ・Django本」になれますように。

techbookfest.org

akiyoko.hatenablog.jp



 

目次

ざっと内容を紹介しますが、詳細には触れていませんので興味がある内容が一つでもあれば実際に読んでみてもらえればと思います。

著者曰く、どの章から読み始めても問題ないそうです(実際、問題ありません)。


 

第1章:Coding Style(コーディングスタイル)
  • p.1〜
  • PEP8 に準拠すべし
  • OSSプロジェクトは1行79文字、その他のプロジェクトは1行99文字制限に
  • 明示的な相対インポートを使うべし
  • Django コーディングスタイル およびその他のガイドラインについて
第2章:The Optimal Django Environment Setup(最適なDjango環境設定)
  • p.13〜
  • 開発環境のセットアップについて
  • 開発環境と本番環境で別々のデータベースを使っている場合は要注意 *3
  • pip と virtualenv を使おう。virtualenvwrapper もオススメ
  • コードを Git や Mercurial でバージョン管理しよう
  • Vagrant + VirtualBox、Docker などを使って開発環境を仮想化する
第3章:How To Lay Out Django Projects(Djangoプロジェクトをどのようにレイアウトすべきか)
  • p.21〜
  • プロジェクトレイアウトのベストプラクティス(リポジトリルート/プロジェクトルート/コンフィグレーションルート)
  • virtualenv の格納場所はプロジェクト外に *4
  • startproject の代わりに、cookiecutter-django コマンドを使って究極の Djangoテンプレートプロジェクトを作成しよう
第4章:Fundamentals of Django App Design(Djangoアプリケーション設計の基本)
  • p.33〜
  • Django app(アプリケーション)を理解しよう。 *5
  • それぞれのアプリケーションは一つのタスクに集中すべき(一文で説明できるようにできるだけ小さく設計すべき)
  • アプリケーション名はメインとなるモデルの複数形にすべし *6
  • アプリケーションに含めるべきモジュール群(とネーミング)
第5章:Settings and Requirements Files(SettingsモジュールとRequirementsファイル)
  • p.41〜
  • 「SECRET_KEY」などのシークレットキー(*7)を除く全ての設定はバージョン管理下に置くべき
  • シークレットキーの設定には環境変数を使う(付録Eを参照)
  • アンチ local_settings.py パターンとして、各環境に合わせた Settingsモジュール(base.py/local.py/staging.py/test.py/production.py)を用意
  • Settingsモジュール内のファイルパスの書き方について
第6章:Model Best Practices(モデルのベストプラクティス)
  • p.63〜
  • django-model-utils の TimeStampedModel や django-extensions はどのプロジェクトでも便利に使える
  • モデルが5個までに収まるようにアプリケーションの大きさを設計する
  • 3種類のモデルの継承方法(abstract base classes, multi-table inheritance, proxy models)のうちどれを使うべきか *8, *9
  • データベースのマイグレーション方法について
  • テーブル設計で非正規化をする前にキャッシングを検討しよう
  • null=True, blank=True の使いどころ
  • models.field.GenericForeignKey はなるべく使わないようにしよう
  • モデルの _meta API について
  • モデルマネージャについて
  • ファットモデル志向(データ処理やビジネスロジックはモデルに集約)
  • モデルがファットになりすぎないように、Mixin やステートレスなヘルパー関数に分離しよう
第7章:Queries and the Database Layer(クエリとデータベース層)
  • p.81〜
  • ビューで単一のオブジェクトを取得する場合は get() の代わりに get_object_or_404() を使う *10
  • ObjectDoesNotExist はどのモデルでも共通で使える
  • get() で複数のオブジェクトが抽出されてしまう場合は MultipleObjectsReturned で判定
  • 遅延評価を理解しよう。長いクエリは読みやすいように分割しよう
  • Django 1.8 から UPPER, LOWER, SUBSTR などの関数が使えるように *11
  • 生の SQL は出来る限り書かないようにする *12
  • デフォルトのトランザクションはクエリごと *13
第8章:Function- and Class-Based Views(関数ベースおよびクラスベースのビュー)
  • p.95〜
  • 関数ベース、クラスベースのビューのどちらを使うべきか? *14
  • URLConf にロジックを書かない(ビューやモデルを使い回せるように分離)
  • アプリケーションレベルの namespace を使うと便利
  • URLConf のビューへの参照は文字列で書かない
第9章:Best Practices for Function-Based Views(関数ベースビューのベストプラクティス)
  • p.109〜
  • 再利用できない、クラスベースビューのように継承が使えない、のが難点
  • HttpRequest を引数にしたヘルパー関数やデコレータを使うことで、ビューをシンプルにできる
第10章:Best Practices for Class-Based Views(クラスベースビューのベストプラクティス)
  • p.117〜
  • 継承構造がややこしいのが難点
  • Mixin を利用しよう *15
  • 代表的な GCBV(generic class-based view)の紹介 *16
  • CBV + ModelForm, CBV + Form の例
  • django-braces の Mixin は GCBV の足りない部分を補完してくれるので便利
第11章:Form Fundamentals(フォームの基本)
  • p.137〜
  • ModelForm を使おう
  • 外部から入力された値はフォームでバリデーションすべし
  • データの更新を伴うリクエストには CSRF対策を施そう
  • Ajaxリクエストには、CSRF対策を施すか、HTTPヘッダに「X-CSRFToken」を設定する
  • django-braces の ModelForm Mixin は便利 *17
  • form.is_valid() の仕組み
第12章:Common Patterns for Forms(フォームのよくあるパターン)
  • p.149〜
  • フォーム関連の便利パッケージについて *18
  • clean() には複数フィールドにまたがる相関チェックを書く
  • clean(), clean_xxx() には、データベースに永続化されたデータを使ったバリデーションを書いても OK
  • Model とほぼ同じフィールドを Form に加えるときは、コピペせずに ModelForm の __init__ で self.fields を参照すればよい
第13章:Templates: Best Practices(テンプレート:ベストプラクティス)
  • p.167〜
  • テンプレートファイルは templates/ にひとまとめにしよう
  • 二層構造にする。それぞれのアプリケーションごとにベースのレイアウトを変えたい場合は(base.html を継承した)三層構造にする
  • "Flat is better than nested"
  • テンプレートレイヤでは必要以上にクエリや処理が実行されないように注意
  • テンプレートは「データを表示する」だけにする
  • ループ内で不必要なクエリが発行されないように select_related を使う *19
  • テンプレートの継承を理解しよう
  • テンプレートファイルやブロックなどの名前には「-」ではなく「_」を使う
  • 他のテンプレートファイルから include されるテンプレートファイル名は「_」で始める
  • CBV を利用した場合、「object_list」という暗黙の変数名が使える
  • テンプレート内では URL のパスはハードコードせずに url タグを使う
  • テンプレートのデバッグには TEMPLATES の「string_if_invalid」オプションの設定を使うと便利
  • 404.html と 500.html のエラーページは最低限用意しておこう
第14章:Template Tags and Filters(テンプレートタグとフィルタ)
  • p.191〜
  • フィルタは単なる関数(ただし引数は二つまでという制限あり)
  • テンプレートタグはデバッグしづらい
  • カスタムテンプレートタグのモジュール名は <app name>_tags.py とする
第15章:Django Templates and Jinja2(DjangoテンプレートとJinja2)
  • p.197〜
  • Django 1.8 から Django template language (DTL) 以外に Jinja2 をサポート
  • DTL と Jinja2 のどちらを使うべきか *20
  • ディレクトリごとに適用するテンプレートエンジンを替えることも可能
第16章:Building REST APIs(REST APIの構築)
  • p.207〜
  • 通常のアプリケーションの所々にAPI用のコードを入れるよりも、全ての serializer, renderer, view が入った REST API に特化したアプリケーションを1つ作ってしまうのがベター
    • アプリケーション名は「apiv4」などバージョンを意識した名前にすべし
    • 小さくてシンプルなプロジェクトでは、REST API のビューは単一の views.py または viewsets.py に集約できる
    • 多数の REST API が含まれる大きなプロジェクトでは、viewset パッケージを作ってその中に記述する
    • ビジネスロジックはできるだけAPIビューから分離すべし
  • 別アプローチとして、URLconf を core/api.py や core/apiv1.py に記述して、ルートURLconf から include する方法も
  • バージョンアップを考慮して、URLパターンは /api/v1/users, /api/v2/users などとするとよい
  • レートリミットは必ず入れるべし
    • Nginx や Apache でも導入することが可能(パフォーマンスもよいが、Pythonコード外で管理することになる)
第17章:Consuming REST APIs(REST APIの使用)
  • p.225〜
  • バックエンドに Django、フロントエンドに React.js、Angular.js、Backbone.js などのモダンな JSフレームワークを利用する
  • CSRF トークンの送信方法に注意
第18章:Tradeoffs of Replacing Core Components(コアコンポーネントを入れ替える場合のトレードオフ)
  • p.237〜
  • よっぽどのことがない限り Django のコアコンポーネントを入れ替えるのはやめよう
第19章:Working With the Django Admin(Django管理サイトを動かす)
  • p.243〜
  • Django 管理サイトはエンドユーザ向けのものではなく、管理者向けのもの
  • 一覧表示を見やすくするために、モデルに __str__() メソッドを用意しよう *21
  • 一覧に表示するフィールドは list_display で宣言する
  • 詳細表示をカスタマイズする場合は、ModelAdmin を継承したクラスを用意
  • どうしても管理サイトの見た目をカスタマイズしたい場合は https://github.com/sehmaschine/django-grappelli:titledjango-grappelli などのスキンを使う
第20章:Dealing with the User Model(Userモデルを扱う)
  • p.255〜
  • Django 1.5 以降でユーザクラスを設定で変更できるようになったため、ユーザクラスを取得するには get_user_model() を使う
  • ユーザモデルに対して一対多・一対一・多対多関連のフィールドを作成する場合は、settings.AUTH_USER_MODEL を使う
  • ユーザモデルをカスタマイズするには django-authtools が便利
  • ユーザモデルにカスタムフィールドを追加するための 3つのオプション *22
第21章:Django's Secret Sauce: Third-Party Packages(Djangoの秘伝のソース:サードパーティのパッケージ)
  • p.263〜
  • 本の中で言及したパッケージの一覧については付録Aを参照
  • 55,000 を超える Python パッケージが PyPI で管理されている *23
  • Django Packages は Django関連のプロダクトが集められている比較サイト
  • 依存パッケージはバージョン番号も指定しておこう
  • パッケージの完成度を見極める方法
  • Cookiecutter で Python/Django プロジェクトのテンプレートを作ろう
  • PyPI に登録する方法について
第22章:Testing Stinks and Is a Waste of Money!(疑わしいものをテストしよう!テストはお金の無駄!)
  • p.283〜
  • カバレッジを取るには coverage.py を使うとよい
  • テストモジュールにはファイル名に「test_」のプレフィックスを付けよう
  • 一つの単体テストメソッドでは一つのことをテストするようにしよう
  • request オブジェクトを生成するには RequestFactory を使おう。ただし、Middleware を通さないといけない場合は工夫が必要
  • データのメンテナンスが大変になるので、フィクスチャは使い過ぎないようにしよう *24
  • Mock ライブラリを使ってモックアウトする *25
  • unittest には便利なアサーションメソッドが多数用意されている *26
  • 統合テスト/CI について
  • unittest の代わりの単体テストツールとして、pytest-django, django-nose も便利 *27
第23章:Documentation: Be Obsessed(ドキュメンテーションせずにはいられない)
  • p.301〜
  • ドキュメントは reStructuredText(RST)形式 で書こう *28
  • .rstファイルは Sphinx で各種フォーマットに変換して出力する
  • どんなドキュメントを作るべきか?
第24章:Finding and Reducing Bottlenecks(ボトルネックの発見と解消)
第25章:Asynchronous Task Queues(非同期タスクキュー)
  • p.319〜
  • Celery, Redis Queue, django-background-tasks のどれを使うべきか?
  • タスクはビューのように小さくせよ
第26章:Security Best Practices(セキュリティのベストプラクティス)
  • p.327〜
  • XSS、CSRF、SQLインジェクション、クリックジャッキング、TLS/HTTPS サポート、HTMLタグの自動エスケープなどが標準装備
  • SECERT_KEY の取り扱いに注意すべし(バージョン管理ツールの管理下にしないように)
  • HTTPS/SSL 化するには django.middleware.security.SecurityMiddleware を使う(ただし、static, media は含まれないので注意)
  • 標準ライブラリの pickle には気をつけよう
  • cookieベースのセッションは使わない
  • クレジットカード番号やパスワードなどのセキュアなデータ入力フィールドを利用する際の注意点 *29
  • 強度の高いパスワードを生成するには django-passwords, django-autoadmin などを利用する
  • アップロードファイルは python-magic でファイルヘッダを確認する
  • Django管理サイトへのIPアドレス制限をする場合は Middleware で判別
第27章:Logging: What’s It For, Anyway?(ロギング:それって何のため?)
  • p.355〜
  • 各ログレベル(CRITICAL/ERROR/WARNING/INFO/DEBUG)の用途
  • ロガーは再利用せずにモジュールごとに用意する
  • Logger.exception() について
  • logutils パッケージが便利
第28章:Signals: Use Cases and Avoidance Techniques(シグナル:ユースケースと回避テクニック)
  • p.365〜
  • シグナルは同期・ブロッキングなので、パフォーマンスを考慮すべし
  • 複数モデルを操作する場合や save後にキャッシュを無効化したい場合などはシグナルを使ってよい
  • models.Manager のメソッドやフォームの cleanメソッドで置き換えられないか検討する
  • 単一モデルを操作している場合は saveメソッド内に書けないか検討する
第29章:What About Those Random Utilities?(ちょっとしたユーティリティはどうしたらいい?)
  • p.371〜
  • 汎用的な共通モジュールは coreアプリケーションに配置しよう
  • 各アプリケーション直下にヘルパーモジュール utils.py(helpers.py)を
  • Django に内蔵されているヘルパーを利用しよう *30, *31, *32
第30章:Deployment: Platforms as a Service(デプロイ:プラットフォーム・アズ・ア・サービス)
  • p.387〜
第31章:Deploying Django Projects(Djangoプロジェクトのデプロイ)
  • p.395〜
第32章:Continuous Integration(継続的インテグレーション)
  • p.411〜
  • テストをスピードアップするための Tips
  • 複数の異なる Python, Django バージョンで検証したい場合は tox を使う
  • Jenkins, Travis-CI, CircleCI などの CIツール/CIサービスを使おう
第33章:The Art of Debugging(デバッグの技術)
  • p.417〜
  • django-debug-toolbar を使おう
  • PDB を使おう *33
  • ファイルアップロードを扱う際のチェックポイント
第34章:Where and How to Ask Django Questions(Djangoに関する質問をする場所と方法)
  • p.427〜
  • 同じ問題を抱えている人がいないかググったり、MLStackOverflow をチェックしてみよう
  • django-users の MLや IRC で直接聞いてみよう
第35章:Closing Thoughts(最後に)
  • p.431〜
  • Django 2.2 がリリースされるまでは次のシリーズは書かない
  • Django 1.8 は LTE なので、しばらくはこの本が使えるでしょう


 

まとめ

「Two Scoops of Django」の著者は、Django を理解するには、フォーム(Form)、モデル(Model)、クラスベースビュー(CBV)について理解することが重要だと言っていますが、私は特に、モデルと Django ORM 周りについての理解がネックになると考えています。


「Two Scoops of Django」は全編英語で、英語が苦手な人には取っ付きにくいかもしれませんが、どの章から読み始めてもよいという性質と堅苦しくないポップな雰囲気から、Django 初級者にピッタリな本だと思います。ポップなタッチながらも、Django の実戦的なノウハウが一から十まできめ細やかに書かれていて、Django アプリのデベロッパーにとって非常に貴重な本に仕上がっています。


英語が苦手でない人には絶対オススメなので、是非読んでみてください!!



明日は、felyce さんの 6日目の記事「DjangoのForm(CreateView、UpdateViewなど)について - Qiita」です。よろしくお願いします。



 

英単語

最後に、「Two Scoops of Django」本に出てきた英単語をピックアップしました。
読むときのお役に立てば。


p.xxxv vet : 吟味する

p.xxxv distill : 抽出する

p.xxxv suffice : 十分である

p.xxxvi errata : 正誤表

p.1 abbreviate : 省略する

p.3 accommodate : 適応する、同意する

p.3 provision : 規定、供給

p.8 It goes without saying : 〜は言うまでもない

p.13 optimal : 最適な

p.13 pitfall : 落とし穴

p.13 identically : 完全に同じように

p.14 without a hitch : 問題なく

p.15 pre-populate : 自動入力する

p.15 familiarize : 習熟させる

p.26 intentional : 故意の

p.34 truncated : 省略された

p.34 in essence : 要するに

p.34 moderate : 適度な

p.35 envision : 心に思い描く

p.36 convention : 慣習、慣例

p.36 dull : つまらない

p.36 correspond with : 〜と一致する

p.36 discouraged : 推奨されない

p.37 when in doubt : 判別がつかないときは

p.37 a modicum of : 少量の

p.41 stay away from : 〜を避ける

p.42 purposefully : 意図的に

p.48 substantially : 実質的に

p.48 without hesitation : 気兼ねなく

p.63 think things through : じっくり物事を考える

p.63 down the road : やがて

p.63 sound : 堅固な、安定した

p.64 ramification : 予期せぬ問題

p.64 sloppy : ずさんな

p.64 derived : 生成された

p.65 concrete : 具象的な

p.65 overlap : 重複部分

p.65 inadvertent : うっかりした

p.65 substantial : 相当の

p.65 traverse : 横断する

p.67 nasty : 不快な

p.67 propagation : 伝播

p.67 aptly : 適切に

p.68 unwieldy : 手に負えない

p.68 bring someone to heel : 〜を従わせる

p.69 prematurely : 時期尚早に

p.69 panacea : 万能薬

p.73 unconstrained : 拘束されない

p.73 akin : 同種の

p.73 comforted : 安心した

p.78 infer : 推察する

p.79 judiciously : 思慮深く

p.81 consistently : 一貫して

p.81 quirk : 奇癖

p.81 cartwheel : 車輪

p.81 unconstrained : 制約されない、とらわれない

P.83 legible : 読みやすい

p.84 mitigate : 和らげる、軽減する

p.84 lean on : 〜に頼る

p.85 shudder : 身震いする

p.86 comparison : 比較

p.86 under the hood : 内部で

p.87 decent : 適切な

p.87 drastically : 抜本的に

p.88 approximate : 〜に近い

p.89 practitioner : 実行者

p.89 acronym : 頭字語

p.89 overhaul : 〜を徹底的に点検する

p.89 modernize : 近代化する

p.90 monumental : (記念碑のように)巨大な

p.90 embarrassing : 厄介な

p.90 crop up : 不意に起こる

P.92 downside : 否定的側面

p.96 err on the side of : 〜し過ぎて失敗する(傾向にある)

p.97 yell : 怒鳴る

p.98 argue : 異議を唱える、主張する

p.98 steer clear of : 〜を避ける

P.98 stick to : 〜に忠実である

p.101 hackery : ハッカー行為

p.101 elaborative : 入念な

p.101 automagically : 魔法のごとく自動的に

p.103 collide : 衝突する

p.104 prevalence : 普及、行き渡ること

p.104 tangible : 明らかな、具体的な

p.105 come into play : 動き始める

p.106 when it comes down to it : いざというときには、ここ一番というときには

p.106 algebra : 代数

p.107 one-off : 一回限りの、単発の

p.108 annoy : 困らせる、イライラさせる

p.108 exploit : 〜を利用する、十分に引き出す

p.108 adhere to : 〜を支持する、忠実に守る

p.109 come at the expense of : 〜を犠牲にして成り立つ、〜という代償で手に入る

p.110 cognitive overload : 認知的過負荷

p.110 arbitrary : 任意の

p.111 in the meantime : その間に

p.113 for once : 今回に限り

p.113 parlance : 専門用語

p.113 out of necessity : 必要に迫られて

p.113 to the point of : 〜するくらいまで

p.113 ubiquitous : 至る所にある

p.115 astute : 抜け目のない

p.117 advent : 出現、到来

p.117 out of the box : 難しい設定などは一切なしで

p.117 address : 対処する、取り組む

p.118 along the line of : 〜に従って

p.122 constrain : 制約する

p.128 catch : 落とし穴、わな

p.128 wire into : 〜に配線する

p.130 queue up : 列を作る

p.131 to recap : 要点をまとめると

p.131 intention : 意図

p.133 readily : すぐに、容易に

p.135 straight-forward : 簡単な、分かりやすい

p.135 in essence : 要するに

p.137 trivial : ささいな

p.137 anguish : 苦悩

p.137 albeit : 〜ではあるが

p.140 alteration : 変更

p.140 idempotent : 冪等の

p.141 capability : 能力、機能

p.141 exempt : 免除された

p.142 in conjunction with : 〜と連動して

p.144 iterate : 〜を反復する

p.144 coerce : 〜を強要する

p.146 streamline : 簡素化する

p.149 extensively : 広範囲に渡って

p.149 explicitly : 明確に

p.152 corruption : (データの)破壊

p.154 corruption : 〜を除いて

p.159 fancy : 手の込んだ

p.161 gross : 不作法な、気持ち悪い

P.167 constrain : 〜を制約する

p.168 tier : 層

p.169 consistent : 矛盾のない、一貫した

p.169 distinctive : 独特の

p.171 eloquently : 雄弁に

p.171 inefficiency : 効率が悪いこと

p.171 redeemable : 商品と交換できる

p.172 redeem : 商品と引き換える

p.173 breakdown : 分析結果

p.173 redemption : 回収

p.177 implied : 暗黙の

p.178 intensive : 集中的な

p.179 consumption : 消費

p.179 distract : 〜の気を散らす

p.179 bluntly : 単刀直入に

p.179 realign : 再編成する

p.185 mnemonic : (記憶を助ける)短い語句

p.186 intuitive : 直感的な

p.189 draw : 呼び物、引き付けるもの

p.189 carry away : 心を奪う

p.189 worse yet : さらに悪いことには

P.189 a blessing in disguise : 災い転じて福となす

p.190 bulky : 大きい

p.190 not to mention : 〜は言うまでもなく

p.191 trait : 特徴

p.191 prone : 〜しがちな

p.191 abuse : 〜を乱用する

p.193 unbearably : 我慢できないほど

p.195 fury : 激怒

p.196 supposedly : おそらく

p.196 chagrin : 残念さ

p.196 obscure : 目立たない

p.196 contention : 主張、論争

p.197 syntactical : 構文の

p.198 First off : 最初に

p.198 advent : 出現

p.198 harmoniously : 平和に

p.199 mitigate : 和らげる

p.200 incorporate : 〜を組み込む

p.207 by design : 計画的に、故意に

p.207 representational : 具象的な

p.207 craft : ~を作る

p.207 definitive : 決定的な

p.207 hindrance : 障害物

p.208 distribute : ~をばらまく、~を分配する

p.208 appropriate : 適切な

p.209 by definition : 定義上は

p.209 idempotent : 冪等の

p.209 in favor of : ~を支持して、~に賛成して

p.211 tailor : ~あつらえる、~を調整する

p.211 wire into : ~に配線する

p.213 suited for : ~向きの

p.213 neatly : きちんと

p.213 hunt down : 追跡して捕まえる

p.213 in contrast to : ~と対照的に、~とは異なり

p.213 it makes sense : 理に適う

p.214 when it comes down to it : いざとなれば

p.214 endorse : 推薦する、支持する

p.214 go for : 当てはまる

p.214 complete with : ~が全部そろった

p.214 myriad : 無数の

p.215 dedicated : 専用の、特化した

p.216 lean on : ~に頼る

p.217 abbreviate : ~を簡潔にする、~を短縮する

p.217 anger : ~を怒らせる

p.217 ample : 十分な

p.218 simultaneously : 同時に

p.218 loosely-coupled : 疎結合の

p.218 preferably : できれば

p.220 grumble : 不平を言う

p.220 analogy : 比喩

p.220 inventory : 在庫

p.221 unfettered : 拘束されない、自由な

p.221 graciously : 快く

p.221 desirous : 望んで

p.222 utter : 全くの

p.222 tier : 層

p.222 tie into : 勢いよくとりかかる

p.222 embrace : ~を利用する、~を採用する

p.223 vantage : 優越

p.225 twofold : 二つ折りの、二つの要素からなる

p.225 landscape : 状況

p.225 evolve : 進化する

p.225 advent : 出現

p.225 controversy : 議論

p.227 weed : 除外する

p.227 chaff : もみ殻、無用のもの

p.229 woe : 苦悩

p.229 latency : 待ち時間

p.229 circumference : 周囲

p.229 noticeable : 容易に気付く

p.229 alienate : 遠ざける

p.229 hiccup : 一時的な中断

p.229 distract : ~の気を散らす

p.230 feasible : 実現可能な

p.230 albeit : ~ではあるが

p.230 trickery : 策略

p.230 geographically : 地理的に

p.230 avenue : 手段

p.230 reliant : 依存している

p.230 mitigate : 軽減する

p.231 suffice : 十分である

p.231 certainly : 確かに

p.231 tempting : 誘惑的な

p.234 straight-forward : 分かりやすい、単純な

p.235 consumption : 消費

p.235 up to par : 基準に達して

p.235 capable : 有能な

p.235 ramp up : 成長させる

p.235 reap : ~を得る

p.235 assess : ~を評価する

p.235 a plethora of : 大量の

p.235 advocate : 推奨する

p.237 hype : 誇大広告、うそ

p.237 sacrifice : 〜を犠牲にする

p.237 prematurely : 時期尚早に

p.238 Franken : フランケンの、遺伝子組換えの

p.238 fad : 流行のもの

p.239 implication : 影響

p.239 compliant : 準拠した

p.240 non-issue : 取るに足りない問題

p.240 swallow : 鵜呑みにする

p.241 take it too far : 行き過ぎる、無理する

P.241 advocate : 支持する、推奨する

p.243 mess around with : 〜をいじり回す

p.243 revoke : 〜を無効にする、〜を取り消す

p.243 surreal : 非現実的な

p.250 oddly : 変に

p.251 venerable : 尊敬すべき、非常に古い

p.251 up-and-comer : 新人

p.252 arcane : 難解な

p.252 account for : 〜を構成する、〜の主要因である

p.252 idiosyncrasy : 特異性

p.252 invariably : いつも、常に

p.252 mind-numbing : 極めて退屈でつまらない

p.255 significantly : 著しく、かなり

p.255 radical : 抜本的な、急進的な

p.257 timid : 臆病な

p.257 intrusive : 煩わしい

p.257 in place : 適所に

p.259 bare-bones : 必要最低限の

p.259 slate : 候補者リスト

p.259 sane : 健全な

p.260 unobtrusive : 控えめな

p.261 dedicated : 専用の

p.263 incorporation : 組み込み、合体

p.265 competent : 有能な

p.265 tragic : 悲惨な

p.265 downfall : 転落、崩壊

p.267 formality : 手続き

p.268 pore over : 〜を熟読する

p.268 be willing to : 喜んで〜しようとする

p.269 submission : 提出

p.270 suffice : 十分である

p.270 bulletproof : 防弾の

p.270 in lieu of : 〜の代わりに

p.271 diverge : 分岐する

p.271 invasive : 侵略的な

p.273 forthcoming : 来たるべき

p.273 purist : 純粋主義者

p.274 be meant to : 〜であることを意図されている

p.274 harness : 利用する、役立てる

p.274 scare off : (怖がらせて)追い払う

p.274 obscenity : わいせつなもの

p.275 permissive : 寛大な

p.275 litigation : 訴訟

p.275 liable : 責任がある

p.275 disclaimer : 免責事項

p.275 liability : 法的責任、損害賠償

p.275 interoperability : 相互運用性

p.276 rewarding : やりがいのある

p.277 burnout : 燃え尽き症候群

p.278 irresponsible : 責任のない

p.278 fault : 〜を責める

p.280 in turn : 次に

p.280 exploit : 〜を悪用する

p.281 invariably : いつも、必ず

p.281 give away : 譲る

p.281 notable : 有名な

p.282 diligence : 不断の努力

p.283 stink : 疑わしいもの、悪臭

p.283 rigorous : 厳しい、厳密な

p.283 adequate : 適正な、適切な

p.285 hence : だから、したがって

p.286 therein : その中に、その点で

p.286 conundrum : 難しい問題

p.288 when possible : 可能であれば

p.290 subtly : 微妙に

p.290 wrestle with : 〜と格闘する

p.290 malignant : 悪意のある

p.290 intruder : 侵入者

p.290 at a distance : 少し離れて

p.290 grief : 厄介、面倒

p.292 undetected : 発見されていない

p.292 alas : ああ、悲しいかな

p.295 remedy : 改善する、是正する

p.296 fragile : 壊れやすい、不安定な

p.296 boredom : 退屈

p.297 error-prone : 間違いを起こしやすい

p.299 mandate : 命令する

p.299 gradual : 段階的な

p.299 bogus : 偽造の

p.299 thus far : ここまでは

p.299 comprehend : 理解する

p.301 That being said : そうは言っても

p.301 can't help but : 〜せずにはいられない

p.305 infrequently : まれに

p.305 tangential : 無関係の、脱線した

p.309 snappy : きびきびした

p.309 excessive : 必要以上の、過度の

p.310 invaluable : すこぶる有益な

p.312 noticeable : 目立つ

p.320 pitch in : 協力する

p.320 supervision : 監督、管理

p.320 Arguably : 議論の余地はあるが、ほぼ間違いなく

p.320 intensive : 集約的な

p.321 overkill : やり過ぎ

p.321 footprint : 足跡、専有面積

p.321 rules of thumb : 経験則

p.322 quirk : 奇癖

p.322 portability : 可搬性

p.323 underlying : 下部の、内在する

p.323 counter-productive : 非生産的な

p.328 malicious : 悪意のある

p.328 sniff : 盗聴する

p.328 up for grabs : 容易に手に入る

p.329 reputable : 信頼できる

p.332 expedite : 〜を早める、〜を迅速に処理する

p.332 malignant : 悪意のある

p.334 beware of : 〜に気をつける

p.335 falsify : 改ざんする

p.335 impersonate : 〜になりすます

p.336 transmit : 送信する

p.338 coerce : 〜を強要する

p.338 grave : 重大な

p.341 arbitrary : 任意の

p.341 be of concern : 心配事である

p.341 adequate : 適切な

p.342 incorporate : 〜を組み込む

p.343 paraphrased : 言い換える

p.343 trivial : ささいな

p.344 fine-grain : 微粒子の

p.344 in conjunction with : 〜と連動して

p.345 concealed : 隠れた

p.345 devastating : 壊滅的な

p.347 venerable : 尊敬すべき

p.347 punctuation : 句読点

p.347 pledge : 誓う

p.348 put up : 用意する、立ち上げる

p.348 overwhelm : 圧倒する、〜をひっくり返す

p.348 snap decision : 即断、見切り発車

p.348 ill-advised : 軽率な

p.349 bonded : 保証付きの

p.351 compromise : 譲歩

p.351 obfuscate : 分かりにくくする

p.351 cumbersome : 扱いにくい

p.352 nigh : ほとんど

p.352 corresponding : 同様の

p.353 diligence : 不断の努力

p.355 What's it for? : それは何に使うの?

p.355 once in a while : たまに

p.355 meticulously : 慎重に

p.356 go-to : 頼りになる

p.356 unpredictable : 予測できない

p.356 might as well : 〜してもいい

p.356 fine-tune : 微調整する

p.356 account for : 〜を構成する

p.356 introspection : 内省

p.357 catastrophic : 壊滅的な

p.357 sparingly : 控えめに

p.358 intruder : 侵入者

p.360 sprinkle : ちりばめる

p.360 debt : 負債

p.360 go overboard : やり過ぎる

p.360 clutter : 〜で溢れさせる

p.363 reduction : 減少

p.365 last resort : 最後の手段

p.365 knot : からまる

p.365 hairball : 毛玉

p.365 untangle : 〜のもつれを解く

p.365 dispatch : 〜を送り出す

p.365 obfuscation : 難読化

p.366 inversion : 逆転

p.370 delve into : 掘り下げる

p.371 general-purpose : 用途の広い

p.372 inevitably : 必然的に

p.372 Synonymous with : 〜と同じ意味の

p.373 brobdingnagian : 巨大な

p.374 indispensable : 不可欠の

p.385 a plethora of : 過多の

p.385 tangle : 混乱

p.411 originator : 創始者

p.411 cohesive : 団結した、まとまりのある

p.412 statically typed : 静的に型付けされた

p.413 oodles : たくさん

p.414 analogue : 類似品

p.415 outweigh : 上回る

p.417 annoying : うるさい

p.418 -naut : 〜の航行者

p.419 in regards to : 〜に関しては

p.419 disastrous : 破滅的な

p.419 essential : 必要不可欠なもの

p.419 frustrating : イライラする

p.422 immensely : 広大に

p.422 embrace : 受け入れる、採用する

p.422 to the fullest : 最大限に

p.423 replicate : 〜を複製する

p.424 nemesis : 悪の根源、因果応報

p.425 haywire : めちゃくちゃな

p.425 utter : ひどい

p.425 unfold : 姿を現す

p.425 aficionado : 熱狂的なファン

p.426 in exchange for : 〜と引き換えに

p.427 get stuck : 行き詰まる

p.427 descriptive : 記述的な

p.428 troll : 煽る

p.428 cranky : 気難しい

p.428 gratitude : 感謝、感謝の気持ち

p.428 go a long way : 大いに役立つ

p.428 make someone's day : 人を幸せな気分にさせる

p.428 fill up : 満たす

p.428 a tidbit of : ちょっとした

p.429 put in : 投資する

p.429 diversity : 多様性

p.429 underrepresent : 過小評価する

p.431 ground : 領域

p.431 starter : 初心者

p.431 as for : 〜に関しては

p.431 genuinely : 心から

p.431 omission : 省略、手抜かり

*1:英語は易しめだと思います

*2:二人は 2013年末に結婚しています。 https://www.pydanny.com/i-married-audrey-roy.html また、cookiecutter-django のコアコミッターとしても有名です。 https://github.com/audreyr/cookiecutter

*3:データベースごとにフィールドタイプの扱い方が違っていたりする

*4:例えば「~/projects/」あるいは「~/.envs/」など。virtualenvwrapper を使っているのであれば「~/.virtualenvs/」など

*5:「Django apps are small libraries designed to represent a single aspect of a project.」とのこと。

*6:推奨しないが、単語と単語の間にはアンダースコアを使ってもよい

*7:AWS・Stripe などの APIキー、OAuth トークンなども含まれる

*8:「abstract base classes」は Metaクラスに「abstract = True」を記述した抽象ベースクラスを継承する方式、「multi-table inheritance」は OneToOneField で複数のテーブルを一対一関連させる方式、「proxy models」は Metaクラスに「proxy = True」を記述した子クラスで継承する方式。 Models | Django documentation | Django

*9:multi-table inheritance 方式は使わないようにしよう!

*10:ヘルパー関数、フォーム、モデルのメソッドなど、ビュー(やビューに直接関連した部品)以外では使わないこと

*11:https://docs.djangoproject.com/en/1.8/ref/models/database-functions/

*12:書くなら raw() で。extra() は使わない

*13:リクエストごとにしたいのであれば、settings.py の「ATOMIC_REQUESTS」の設定を True にする。ビュー中の特定の範囲にのみトランザクションを適用する場合は「with transaction.non_atomic_requests()」で囲む

*14:複雑にカスタマイズしたい場合だけ関数ベースのビューを使えばよい

*15:Mixin を自作するときは Python object 型を継承し、複雑に継承させない設計にする

*16:View, RedirectView, TemplateView, ListView, DetailView, FormView, CreateView, UpdateView, DeleteView, Generic date views が紹介されている

*17:https://django-braces.readthedocs.io/en/latest/form.html#userformkwargsmixin:UserFormKwargsMixin, https://django-braces.readthedocs.io/en/latest/form.html#userkwargmodelformmixin:UserKwargModelFormMixin など

*18:django-floppyforms, django-crispy-forms, django-forms-bootstrap が紹介されている

*19:select_related の詳細については、過去記事「Django ORM の select_related, prefetch_related の挙動を詳しく調べてみた - akiyoko blog」を参照

*20:パフォーマンスの問題がある場合以外は DTL のままでよい

*21:Python 2.7 の場合は python_2_unicode_compatible デコレータを使うか、__unicode__() メソッドを用意する

*22:AbstractUser を継承する方法、AbstractBaseUser を継承する方法、ユーザモデルに対して一対多・一対一・多対多関連のフィールドを作成する方法

*23:「パイピーアイ」と発音する。pip はここからパッケージを検索する

*24:代わりに、factory boy などのツールを使おう

*25:過去記事「Python で MagicMock を使う - akiyoko blog」を参照

*26:https://docs.python.org/2/library/unittest.html#assert-methods

*27:過去記事「akiyoko.hatenablog.jp」を参照

*28:Python, Django 界隈ではあまり使われないが、Markdown 形式も人気。なお、Pandoc を使えばマークアップを自由に変換可能

*29:Djangoフォームで autocomplete を off にする、django.forms.PasswordInput を使うなど

*30:ただし、django.utils 内のモジュールは内部向けに作られたものでバージョンによって挙動が変わるので注意。詳しくは Django Utils | Django documentation | Django を参照

*31:django.core.exceptions のいくつかのビルトイン例外クラスが利用可能

*32:シリアライズ/デシリアライズには django.core.serializers が使える

*33:過去記事「PDB QUEST ~ pdb のショートカットはドラクエ風に覚えよう ~ - akiyoko blog」を参照

Mezzanine に Cartridge 0.12 を導入してみる

この前 4.2.2 にアップデートした Mezzanine サイトに、Cartridge 0.12 を導入してみました。


Mezzanine は、Python製の WordPress風フルスタックCMSフレームワークですが、一方の Cartridge は、Mezzanine 専用に作られた、Mezzanine に ECサイト機能を搭載するためのアプリケーションです。 *1


イメージとしてはこんな感じです。

f:id:akiyoko:20161120180920p:plain


具体的には、商品登録やセール設定、ディスカウント設定、注文管理などの機能を備えたバックオフィス(Django Admin を拡張)、ショッピングカート、PayPal および Stripe に対応した決済モジュール(プラガブルに変更可能)などの機能が付きます。


f:id:akiyoko:20161120182828p:plain
(ショッピングカート機能)


f:id:akiyoko:20161120184415p:plain
(バックオフィス機能)


 

1. 導入手順

Mezzanine に Cartridge 0.12 を導入する手順は以下の通りです。


Cartridge 0.12.0 をインストールします。

(akiyokoproject)$ pip install cartridge==0.12.0

Successfully installed cartridge-0.12.0 pyPdf2-1.26.0 reportlab-3.3.0 xhtml2pdf-0.0.6


config/settings.py に Cartridge 用の設定を追記します。

--- a/config/settings.py
+++ b/config/settings.py
@@ -7,6 +7,76 @@ from django.utils.translation import ugettext_lazy as _


 ######################
+# CARTRIDGE SETTINGS #
+######################
+
+# The following settings are already defined in cartridge.shop.defaults
+# with default values, but are common enough to be put here, commented
+# out, for conveniently overriding. Please consult the settings
+# documentation for a full list of settings Cartridge implements:
+# http://cartridge.jupo.org/configuration.html#default-settings
+
+# Sequence of available credit card types for payment.
+# SHOP_CARD_TYPES = ("Mastercard", "Visa", "Diners", "Amex")
+
+# Setting to turn on featured images for shop categories. Defaults to False.
+# SHOP_CATEGORY_USE_FEATURED_IMAGE = True
+
+# Set an alternative OrderForm class for the checkout process.
+# SHOP_CHECKOUT_FORM_CLASS = 'cartridge.shop.forms.OrderForm'
+
+# If True, the checkout process is split into separate
+# billing/shipping and payment steps.
+# SHOP_CHECKOUT_STEPS_SPLIT = True
+
+# If True, the checkout process has a final confirmation step before
+# completion.
+# SHOP_CHECKOUT_STEPS_CONFIRMATION = True
+
+# Controls the formatting of monetary values accord to the locale
+# module in the python standard library. If an empty string is
+# used, will fall back to the system's locale.
+# SHOP_CURRENCY_LOCALE = ""
+
+# Dotted package path and name of the function that
+# is called on submit of the billing/shipping checkout step. This
+# is where shipping calculation can be performed and set using the
+# function ``cartridge.shop.utils.set_shipping``.
+# SHOP_HANDLER_BILLING_SHIPPING = \
+#                       "cartridge.shop.checkout.default_billship_handler"
+
+# Dotted package path and name of the function that
+# is called once an order is successful and all of the order
+# object's data has been created. This is where any custom order
+# processing should be implemented.
+# SHOP_HANDLER_ORDER = "cartridge.shop.checkout.default_order_handler"
+
+# Dotted package path and name of the function that
+# is called on submit of the payment checkout step. This is where
+# integration with a payment gateway should be implemented.
+# SHOP_HANDLER_PAYMENT = "cartridge.shop.checkout.default_payment_handler"
+
+# Sequence of value/name pairs for order statuses.
+# SHOP_ORDER_STATUS_CHOICES = (
+#     (1, "Unprocessed"),
+#     (2, "Processed"),
+# )
+
+# Sequence of value/name pairs for types of product options,
+# eg Size, Colour. NOTE: Increasing the number of these will
+# require database migrations!
+# SHOP_OPTION_TYPE_CHOICES = (
+#     (1, "Size"),
+#     (2, "Colour"),
+# )
+
+# Sequence of indexes from the SHOP_OPTION_TYPE_CHOICES setting that
+# control how the options should be ordered in the admin,
+# eg for "Colour" then "Size" given the above:
+# SHOP_OPTION_ADMIN_ORDER = (2, 1)
+
+
+######################
 # MEZZANINE SETTINGS #
 ######################

@@ -21,7 +91,9 @@ from django.utils.translation import ugettext_lazy as _
 #
 # ADMIN_MENU_ORDER = (
 #     ("Content", ("pages.Page", "blog.BlogPost",
-#        "generic.ThreadedComment", (_("Media Library"), "media-library"),)),
+#        "generic.ThreadedComment", (_("Media Library"), "fb_browse"),)),
+#     (_("Shop"), ("shop.Product", "shop.ProductOption", "shop.DiscountCode",
+#        "shop.Sale", "shop.Order")),
 #     ("Site", ("sites.Site", "redirects.Redirect", "conf.Setting")),
 #     ("Users", ("auth.User", "auth.Group",)),
 # )
@@ -247,6 +319,7 @@ INSTALLED_APPS = (
     "mezzanine.core",
     "mezzanine.generic",
     "mezzanine.pages",
+    "cartridge.shop",
     "mezzanine.blog",
     "mezzanine.forms",
     "mezzanine.galleries",
@@ -272,6 +345,7 @@ MIDDLEWARE_CLASSES = (
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',

+    "cartridge.shop.middleware.ShopMiddleware",
     "mezzanine.core.request.CurrentRequestMiddleware",
     "mezzanine.core.middleware.RedirectFallbackMiddleware",
     "mezzanine.core.middleware.TemplateForDeviceMiddleware",
diff --git a/config/urls.py b/config/urls.py
index c67fd17..1b3b87c 100644
--- a/config/urls.py
+++ b/config/urls.py
@@ -8,6 +8,8 @@ from django.views.i18n import set_language
 from mezzanine.core.views import direct_to_template
 from mezzanine.conf import settings

+from cartridge.shop.views import order_history
+

 admin.autodiscover()

@@ -27,6 +29,10 @@ if settings.USE_MODELTRANSLATION:
     ]

 urlpatterns += [
+    # Cartridge URLs.
+    url("^shop/", include("cartridge.shop.urls")),
+    url("^account/orders/$", order_history, name="shop_order_history"),
+
     # We don't want to presume how your homepage works, so here are a
     # few patterns you can use to set it up.

@@ -93,7 +99,7 @@ urlpatterns += [
     # Note that for any of the various homepage patterns above, you'll
     # need to use the ``SITE_PREFIX`` setting as well.

-    # ("^%s/" % settings.SITE_PREFIX, include("mezzanine.urls"))
+    # url("^%s/" % settings.SITE_PREFIX, include("mezzanine.urls"))

 ]

diff --git a/requirements.txt b/requirements.txt
index 0c4e494..4d43fbe 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
 Mezzanine==4.2.2
+cartridge==0.12.0
 django-ses

追加した内容については、Mezzanine と Cartridge を同時にインストールするコマンドを叩いたときの settings.py の差分を参照しました(詳しくは後述)。


マイグレーションを実行します。

(akiyokoproject)$ python manage.py makemigrations
No changes detected

(akiyokoproject)$ python manage.py migrate
System check identified some issues:

WARNINGS:
?: (mysql.W002) MySQL Strict Mode is not set for database connection 'default'
	HINT: MySQL's Strict Mode fixes many data integrity problems in MySQL, such as data truncation upon insertion, by escalating warnings into errors. It is strongly recommended you activate it. See: https://docs.djangoproject.com/en/1.10/ref/databases/#mysql-sql-mode
Operations to perform:
  Apply all migrations: admin, auth, blog, conf, contenttypes, core, django_comments, forms, galleries, generic, pages, redirects, sessions, shop, sites, twitter
Running migrations:
  Applying shop.0001_initial... OK
  Applying shop.0002_auto_20141227_1331... OK
  Applying shop.0003_emailfield... OK
  Applying shop.0004_productimage_file_field... OK
  Applying shop.0005_auto_20150527_1127... OK
  Applying shop.0006_auto_20150916_0459... OK
  Applying shop.0007_auto_20150921_2323... OK

shop 関連のテーブルが作成されました。




以下の Cartridge のテンプレートを、templates/ および custom/ 配下にコピーします。
(例によって、admin 関連のテンプレートは修正しないため、コピー対象に含めていません。)

templates/accounts/account_profile_update.html
templates/email/base.html
templates/email/order_receipt.html
templates/email/order_receipt.txt
templates/email/receipt.html
templates/email/receipt_rtl.html
templates/pages/category.html
templates/shop/base.html
templates/shop/billing_shipping.html
templates/shop/cart.html
templates/shop/checkout.html
templates/shop/complete.html
templates/shop/confirmation.html
templates/shop/includes/order_details.html
templates/shop/includes/order_details_rtl.html
templates/shop/includes/order_totals.html
templates/shop/includes/order_totals.txt
templates/shop/includes/payment_fields.html
templates/shop/includes/user_panel.html
templates/shop/order_history.html
templates/shop/order_invoice.html
templates/shop/order_invoice_pdf.html
templates/shop/payment.html
templates/shop/product.html
templates/shop/wishlist.html

custom/templates/accounts/account_profile_update.html
custom/templates/email/base.html
custom/templates/email/order_receipt.html
custom/templates/email/order_receipt.txt
custom/templates/email/receipt.html
custom/templates/email/receipt_rtl.html
custom/templates/pages/category.html
custom/templates/shop/base.html
custom/templates/shop/billing_shipping.html
custom/templates/shop/cart.html
custom/templates/shop/checkout.html
custom/templates/shop/complete.html
custom/templates/shop/confirmation.html
custom/templates/shop/includes/order_details.html
custom/templates/shop/includes/order_details_rtl.html
custom/templates/shop/includes/order_totals.html
custom/templates/shop/includes/order_totals.txt
custom/templates/shop/includes/payment_fields.html
custom/templates/shop/includes/user_panel.html
custom/templates/shop/order_history.html
custom/templates/shop/order_invoice.html
custom/templates/shop/order_invoice_pdf.html
custom/templates/shop/payment.html
custom/templates/shop/product.html
custom/templates/shop/wishlist.html


最後に、プロセスを再起動して完了です。

(akiyokoproject)$ sudo supervisorctl restart all

 

細かい話

2.1. ライブラリの差分

Cartridge 導入前のライブラリ一覧

awscli (1.11.10)
beautifulsoup4 (4.5.1)
bleach (1.4.3)
boto (2.41.0)
botocore (1.4.67)
chardet (2.3.0)
colorama (0.3.7)
Django (1.10.3)
django-appconf (1.0.2)
django-compressor (2.0)
django-contrib-comments (1.7.3)
django-ses (0.8.0)
docutils (0.12)
filebrowser-safe (0.4.6)
future (0.16.0)
futures (3.0.5)
grappelli-safe (0.4.5)
gunicorn (19.6.0)
html5lib (0.9999999)
jmespath (0.9.0)
Mezzanine (4.2.2)
MySQL-python (1.2.5)
oauthlib (2.0.0)
Pillow (3.4.2)
pip (8.1.2)
psycopg2 (2.6.2)
pyasn1 (0.1.9)
python-dateutil (2.5.3)
python-memcached (1.58)
pytz (2016.7)
rcssmin (1.0.6)
requests (2.11.1)
requests-oauthlib (0.7.0)
rjsmin (1.0.12)
rsa (3.4.2)
s3transfer (0.1.9)
setproctitle (1.1.10)
setuptools (24.0.2)
six (1.10.0)
tzlocal (1.3)
wheel (0.29.0)

導入後のライブラリ一覧

$ pip list
awscli (1.11.10)
beautifulsoup4 (4.5.1)
bleach (1.4.3)
boto (2.41.0)
botocore (1.4.67)
Cartridge (0.12.0)
chardet (2.3.0)
colorama (0.3.7)
Django (1.10.3)
django-appconf (1.0.2)
django-compressor (2.0)
django-contrib-comments (1.7.3)
django-ses (0.8.0)
docutils (0.12)
filebrowser-safe (0.4.6)
future (0.16.0)
futures (3.0.5)
grappelli-safe (0.4.5)
gunicorn (19.6.0)
html5lib (0.9999999)
jmespath (0.9.0)
Mezzanine (4.2.2)
MySQL-python (1.2.5)
oauthlib (2.0.0)
Pillow (3.4.2)
pip (8.1.2)
psycopg2 (2.6.2)
pyasn1 (0.1.9)
PyPDF2 (1.26.0)
python-dateutil (2.5.3)
python-memcached (1.58)
pytz (2016.7)
rcssmin (1.0.6)
reportlab (3.3.0)
requests (2.11.1)
requests-oauthlib (0.7.0)
rjsmin (1.0.12)
rsa (3.4.2)
s3transfer (0.1.9)
setproctitle (1.1.10)
setuptools (24.0.2)
six (1.10.0)
tzlocal (1.3)
wheel (0.29.0)
xhtml2pdf (0.0.6)


 

2.2. config/setting.py の差分

Mezzanine と Cartridge を同時にインストールするには、

mezzanine-project -a cartridge config .

というコマンドを叩けばよいのですが、Mezzanine だけをインストールした場合と Mezzanine と Cartridge を同時にインストールした場合とで、自動生成される settings.py の差分を確認してみました。


1)(Cartridge 無しの)通常 Mezzanine インストール
sudo mkdir -p /opt/webapps/mezzproject
sudo chown -R `whoami`. /opt/webapps
cd /opt/webapps/mezzproject/
mezzanine-project config .
2)Cartridge 付きの Mezzanine インストール
sudo mkdir -p /opt/webapps/mezzproject_cartridge
sudo chown -R `whoami`. /opt/webapps
cd /opt/webapps/mezzproject_cartridge/
mezzanine-project -a cartridge config .


(diff 関連の参考リンク)

$ diff -r -q /opt/webapps/mezzproject /opt/webapps/mezzproject_cartridge
$ diff -r -u /opt/webapps/mezzproject /opt/webapps/mezzproject_cartridge | vi -R -

-q : ファイル名だけ表示
-r : ディレクトリ比較
-u : ユニファイド形式
| vi -R - : vim の機能を使って色分け

ちなみに、進んでいる方を第二引数にした方がよさそうです(+ で diff表示されるので)。

$ diff -r -u /opt/webapps/mezzproject /opt/webapps/mezzproject_cartridge
Only in /opt/webapps/mezzproject_cartridge/config: dev.db
diff -r -u /opt/webapps/mezzproject/config/local_settings.py /opt/webapps/mezzproject_cartridge/config/local_settings.py
--- /opt/webapps/mezzproject/config/local_settings.py   2016-11-03 03:06:58.160419703 +0000
+++ /opt/webapps/mezzproject_cartridge/config/local_settings.py 2016-11-03 03:07:09.000000000 +0000
@@ -8,8 +8,8 @@
 DEBUG = True

 # Make these unique, and don't share it with anybody.
-SECRET_KEY = "k8dd+%#@kw6vh1a-#k(l1agb=@kibpbz&s$7nk)l@s06gz*gx9"
-NEVERCACHE_KEY = "15fmx+kop%oxps(1w3uq5i1btc#3t=+t!#=ax@3!4^@$s%4gu2"
+SECRET_KEY = "(#eotia6m9*m(9vt)@u7^6@)e$f44c(4)4n9*((o%(^u3!u&cw"
+NEVERCACHE_KEY = "n66suh9!=@^-_6^toe)s_z)c$0g70e%in1ifzo7w%&uf&b#z(y"

 DATABASES = {
     "default": {
diff -r -u /opt/webapps/mezzproject/config/settings.py /opt/webapps/mezzproject_cartridge/config/settings.py
--- /opt/webapps/mezzproject/config/settings.py 2016-11-03 03:06:58.164419703 +0000
+++ /opt/webapps/mezzproject_cartridge/config/settings.py   2016-11-03 03:07:09.000000000 +0000
@@ -7,6 +7,76 @@


 ######################
+# CARTRIDGE SETTINGS #
+######################
+
+# The following settings are already defined in cartridge.shop.defaults
+# with default values, but are common enough to be put here, commented
+# out, for conveniently overriding. Please consult the settings
+# documentation for a full list of settings Cartridge implements:
+# http://cartridge.jupo.org/configuration.html#default-settings
+
+# Sequence of available credit card types for payment.
+# SHOP_CARD_TYPES = ("Mastercard", "Visa", "Diners", "Amex")
+
+# Setting to turn on featured images for shop categories. Defaults to False.
+# SHOP_CATEGORY_USE_FEATURED_IMAGE = True
+
+# Set an alternative OrderForm class for the checkout process.
+# SHOP_CHECKOUT_FORM_CLASS = 'cartridge.shop.forms.OrderForm'
+
+# If True, the checkout process is split into separate
+# billing/shipping and payment steps.
+# SHOP_CHECKOUT_STEPS_SPLIT = True
+
+# If True, the checkout process has a final confirmation step before
+# completion.
+# SHOP_CHECKOUT_STEPS_CONFIRMATION = True
+
+# Controls the formatting of monetary values accord to the locale
+# module in the python standard library. If an empty string is
+# used, will fall back to the system's locale.
+# SHOP_CURRENCY_LOCALE = ""
+
+# Dotted package path and name of the function that
+# is called on submit of the billing/shipping checkout step. This
+# is where shipping calculation can be performed and set using the
+# function ``cartridge.shop.utils.set_shipping``.
+# SHOP_HANDLER_BILLING_SHIPPING = \
+#                       "cartridge.shop.checkout.default_billship_handler"
+
+# Dotted package path and name of the function that
+# is called once an order is successful and all of the order
+# object's data has been created. This is where any custom order
+# processing should be implemented.
+# SHOP_HANDLER_ORDER = "cartridge.shop.checkout.default_order_handler"
+
+# Dotted package path and name of the function that
+# is called on submit of the payment checkout step. This is where
+# integration with a payment gateway should be implemented.
+# SHOP_HANDLER_PAYMENT = "cartridge.shop.checkout.default_payment_handler"
+
+# Sequence of value/name pairs for order statuses.
+# SHOP_ORDER_STATUS_CHOICES = (
+#     (1, "Unprocessed"),
+#     (2, "Processed"),
+# )
+
+# Sequence of value/name pairs for types of product options,
+# eg Size, Colour. NOTE: Increasing the number of these will
+# require database migrations!
+# SHOP_OPTION_TYPE_CHOICES = (
+#     (1, "Size"),
+#     (2, "Colour"),
+# )
+
+# Sequence of indexes from the SHOP_OPTION_TYPE_CHOICES setting that
+# control how the options should be ordered in the admin,
+# eg for "Colour" then "Size" given the above:
+# SHOP_OPTION_ADMIN_ORDER = (2, 1)
+
+
+######################
 # MEZZANINE SETTINGS #
 ######################

@@ -21,7 +91,9 @@
 #
 # ADMIN_MENU_ORDER = (
 #     ("Content", ("pages.Page", "blog.BlogPost",
-#        "generic.ThreadedComment", (_("Media Library"), "media-library"),)),
+#        "generic.ThreadedComment", (_("Media Library"), "fb_browse"),)),
+#     (_("Shop"), ("shop.Product", "shop.ProductOption", "shop.DiscountCode",
+#        "shop.Sale", "shop.Order")),
 #     ("Site", ("sites.Site", "redirects.Redirect", "conf.Setting")),
 #     ("Users", ("auth.User", "auth.Group",)),
 # )
@@ -243,6 +315,7 @@
     "mezzanine.core",
     "mezzanine.generic",
     "mezzanine.pages",
+    "cartridge.shop",
     "mezzanine.blog",
     "mezzanine.forms",
     "mezzanine.galleries",
@@ -251,6 +324,7 @@
     # "mezzanine.mobile",
 )

+
 # List of middleware classes to use. Order is important; in the request phase,
 # these middleware classes will be applied in the order given, and in the
 # response phase the middleware will be applied in reverse order.
@@ -267,12 +341,15 @@
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',

+    "cartridge.shop.middleware.ShopMiddleware",
     "mezzanine.core.request.CurrentRequestMiddleware",
     "mezzanine.core.middleware.RedirectFallbackMiddleware",
     "mezzanine.core.middleware.TemplateForDeviceMiddleware",
     "mezzanine.core.middleware.TemplateForHostMiddleware",
     "mezzanine.core.middleware.AdminLoginInterfaceSelectorMiddleware",
     "mezzanine.core.middleware.SitePermissionMiddleware",
+    # Uncomment the following if using any of the SSL settings:
+    # "mezzanine.core.middleware.SSLRedirectMiddleware",
     "mezzanine.pages.middleware.PageMiddleware",
     "mezzanine.core.middleware.FetchFromCacheMiddleware",
 )
diff -r -u /opt/webapps/mezzproject/config/urls.py /opt/webapps/mezzproject_cartridge/config/urls.py
--- /opt/webapps/mezzproject/config/urls.py 2016-11-03 03:06:58.160419703 +0000
+++ /opt/webapps/mezzproject_cartridge/config/urls.py   2016-11-03 03:07:09.000000000 +0000
@@ -4,10 +4,11 @@
 from django.conf.urls.i18n import i18n_patterns
 from django.contrib import admin
 from django.views.i18n import set_language
-
 from mezzanine.core.views import direct_to_template
 from mezzanine.conf import settings

+from cartridge.shop.views import order_history
+

 admin.autodiscover()

@@ -27,6 +28,11 @@
     ]

 urlpatterns += [
+
+    # Cartridge URLs.
+    url("^shop/", include("cartridge.shop.urls")),
+    url("^account/orders/$", order_history, name="shop_order_history"),
+
     # We don't want to presume how your homepage works, so here are a
     # few patterns you can use to set it up.

@@ -93,7 +99,7 @@
     # Note that for any of the various homepage patterns above, you'll
     # need to use the ``SITE_PREFIX`` setting as well.

-    # ("^%s/" % settings.SITE_PREFIX, include("mezzanine.urls"))
+    # url("^%s/" % settings.SITE_PREFIX, include("mezzanine.urls"))

 ]

Only in /opt/webapps/mezzproject_cartridge: .DS_Store


以上を踏まえて、内容を追加しました。

*1:Mezzanine 公式ページには「Ecommerce / Shopping cart module」と紹介されています。

IPA「情報セキュリティマネジメント試験」に一夜漬けで合格するためのたった二つの勉強法

f:id:akiyoko:20161117002302j:plain

こんにちは、akiyoko です。


先月の IPA(情報処理推進機構)の秋試験で、「情報セキュリティマネジメント試験」に 一夜漬けで合格 することができました!!


パチパチパチ!


先日合格発表があり、めでたく合格できたことを確認しました。

f:id:akiyoko:20161115005143p:plain


試験対策としては、試験前日に数時間やっただけです。
成績照会の結果を見てみると、午前が90点・午後が88点だったので(合格基準は午前・午後それぞれ 60点)余裕の合格と言ってもいいでしょう。



そもそも、「情報セキュリティマネジメント試験」(略号「SG」)は、2016年(平成28年)の春試験からスタートした新しい国家試験(情報処理技術者試験)です。


高度化する IT、多様化・複雑化するサイバー攻撃や脅威に対応できる情報セキュリティマネジメントを担う人材がますます求められているという時代背景に合わせて創設された、いわば「今必要な人材のための新しい資格」とも言えます。


共通キャリア・スキルフレームワーク(CCSF)レベル 2相当なので、他の試験で言うと「基本情報技術者試験」(FE)と同程度のレベルということになります。

f:id:akiyoko:20161115010733p:plain
(試験区分 「IPA 独立行政法人 情報処理推進機構:試験要綱 Ver3.0」より) *1


難易度としてはぶっちゃけ、IT業界で長く働いていればそんなに難しくない試験だと思いますが、セキュリティ関連の専門用語・専門知識が必要になるので、それらの分野が苦手な人はそれなりに勉強が必要になってきます。

私見としては、受験資格(条件)は特に無く *2、年に二回(4月と10月)実施されるため受験チャンスも多く、文章記述の問題が無く(全てマークシート方式)、試験時間も比較的短く(午前と午後で90分ずつ)、受験料もリーズナブル(5,700円)で、気軽に受験できる国家資格だと思います。

時間区分 午前 午後
試験時間 90分 90分
出題形式 多肢選択式(四肢択一) 多肢選択式
合格基準 60点(100点満点) 60点(100点満点)


こちらの公式ページから、サンプル問題(午前・午後)と平成28年度春期試験の問題、平成28年度春期試験の問題をチェックすることができますので、気になる方は一度チラ見してみてください。




さて、この「情報セキュリティマネジメント試験」の受験を勧める対象者としては、

  • 業務で個人情報を取り扱う全ての方
  • 業務部門・管理部門で情報管理を担当する全ての方
  • 外部委託先に対する情報セキュリティ評価・確認を行う全ての方
  • 情報セキュリティ管理の知識・スキルを身に付けたい全ての方
  • パス(ITパスポート試験)合格から、さらにステップアップしたい全ての方

と書いてありますが(*3)、実際に受験会場でざっと見回した限りでは、40代〜50代の管理職っぽい面々もちらほら見受けられました。予想以上に平均年齢が高い印象です。会社から「セキュリティの資格を取れ」と言われていたけど「情報処理安全確保支援士試験」(旧セキュリティスペシャリスト試験)までは手が出せなかった層にある程度のニーズがあるのかもしれません。





 

勉強法①:午前対策は「過去問よりも参考書」

私はその他の資格試験では一貫して「過去問絶対主義」だったのですが、この試験だけは勝手が違うようです。


何と、他の IPA試験だったらそのままの問題が何問か出るはずの過去問から、一問も出なかったのです。

その代わり、私のチェックした限りで、基本情報技術者試験の午前問題から4問、応用情報技術者試験の午前問題から2問、全く同じ問題が出題されていました。


今回買った参考書はこの本なのですが、振り返ってみると大当たりでした。まるっきり同じ過去問が出題されたその6問が練習問題として含まれていたので、この本一冊と心中してしまってもよいでしょう。

(私が買ったのは「2016年秋期版」でしたが、新しいものに差し替えました)


もちろん、全く同じ問題ではなくても似たような問題(例えば「rootkit」という用語を知っていれば答えられる、等)がいくつか出題されていたので、もちろん過去問もしっかりやっておくべきでしょう。


ということで午前対策は、過去問をひと通り、プラス参考書をざっと通し読みするだけ で大丈夫です。合計で 3〜4時間くらいでしょうか。




なお、参考書を Kindle で買うのか、紙の本で買うのかという議論がありますが、正直なところ、一長一短かと思います。今回は、試験対策本として初めて Kindle 版を買ってみたのですが、私はどちらかと言うと紙の本の方が合ってるかなぁという感じです。


<メリット>

  • 持ち運びに便利
  • ちょっとだけ安い
  • 通勤電車で読んでいても恥ずかしくない(!)
  • 直前に読んでいても「必死に勉強している感」を隠せる(!!)

<デメリット>

  • ページを行ったり来たりするのが面倒(特に、過去問の問題と解答・解説)


 

勉強法②:午後対策は「図を描きながらストーリーを理解」

午後は、6〜8ページほどの長文を読んで多肢選択式の設問に答えるタイプの問題が 3問も待ち構えています。


長文を読むのは結構つらいです。文章が長いので、読み終わる頃には前半部分の登場人物や所属なんかを忘れてしまっていることも。。

そこで、多少時間は掛かってしまいますが、下のような組織構造を表した図を描いて全体像をメモしながら状況を把握するのがよいでしょう。

f:id:akiyoko:20161116021359p:plain

コツとしては、問題が違ってもだいたい似たような構成になるはず(情報システム部があって情報セキュリティ責任者とリーダーがいる、等)なので、パターンに慣れることが重要です。


また、問題文にはお決まりの「起承転結」があるので、ストーリーの流れを図の状況と合わせて理解していきましょう。


例えば、こんな感じで問題文が書かれているはずです。

《 起 》 企業や組織の業務、および情報システム部についての紹介
《 承 》 事前のセキュリティ対策や企業や組織個別の事情など
《 転 》 セキュリティ事故発生!!
《 結 》 セキュリティ責任者による事態の収束とセキュリティ対策の評価・改善


どんなセキュリティ事故が起きても、必ず最後はハッピーエンドで終わります。


午後対策は、「組織図」と「ストーリーの流れ」に注目しながら過去問一回分を解けば十分 でしょう。



 

まとめ

午前対策としては、上で紹介した「情報処理教科書 情報セキュリティマネジメント 2016年秋期」を買って、数時間でざっとひと通り読み込んでおきましょう。頻出するセキュリティ用語・知識を網羅しておくことがキーポイントになりますが、この「情報セキュリティマネジメント試験」では他試験(基本情報技術者試験および応用情報技術者試験)の過去問がそのまま出ることが多いので、分からない問題はいっそのこと丸暗記してしまってもよいでしょう(あくまで一夜漬けとして)。


午後対策としては、「図を描きながらストーリーを理解」することを挙げました。問題文は長いですが、ストーリーの流れや組織図はパターン化されていますので、過去問で少し慣れてしまえば、あとは恐れるに足りません。


これであなたも来春には「情報セキュリティマネジメント試験」合格ですね!!

*1:2017年の春試験から、「情報セキュリティスペシャリスト試験」が「情報処理安全確保支援士試験」となり、登録制になるのがこれまでと大きく異なります。

*2:年齢や国籍等を含め受験する上での制限はありません。また、どの試験区分からでも受験いただけます。(「IPA 独立行政法人 情報処理推進機構:情報処理技術者試験:よくある質問」より)

*3:情報セキュリティマネジメント試験 より

「D3.jsで学ぶデータビジュアライゼーション」に参加してきました

会場

Twitter Japan
東京都中央区京橋3−1−1 東京スクエアガーデン19階


(参考)Twitter Japan に行ってきた! - 941::blog



Twitter

twitter.com


 

全体の感想など

今年の 6/28 に D3.js がバージョン 4系にメジャーバージョンアップしたとのことですが、バージョン 3 系がリリースされたのが 2012年12月だったので、およそ 3年半ぶりのメジャーバージョンアップとなったようです。そういえば、二年ほど前に個人的にいろいろ試していたバージョンも 3.3 or 3.4 でした。だいぶ息の長いバージョンになりましたね。サンプルやドキュメント、プラグインがまだ追い付いていないらしいので、現場ではまだしばらくはバージョン 3系が使われそうです。


<過去記事>
akiyoko.hatenablog.jp


後半の発表については、Python と D3.js を連携させるときのノウハウ(REST API を使うとか)だったり、どういうふうに役割分担させたか(例えば、データ検索と前処理は Python / NumPy とか)だったり、バックエンドに Python を使うことの明確なメリット(データ件数とレスポンスタイムの比較)だったりを期待していたので、少し思惑とは違った内容でした。


もちろん個人的にいくつか得るものがありましたし、参加者も多くて全体的な雰囲気も活気があったように見受けられ、データビジュアライゼーションはまだまだ人気分野だと感じました。

なお、今回は有料イベント(1,000円)でした。




 

「細かすぎて伝わらないD3 ver.4の話」

清水 正行氏

(参考)





 

「PythonとD3.jsで作るデータベースの活用を広げる可視化アプリケーション開発」

オーイシ ナオヤ氏


  • 生命科学関連データベースの利用促進のための Webサービス開発
  • AOE: http://aoe.dbcls.jp/
    • 遺伝子発現データベースの可視化サービス
  • 使っているもの
  • Tableau, Spotfire じゃダメなの?? 何で Webサービス?
  • SPARQL Query Filter
  • データが大きい場合や、独自のアルゴリズムで統計処理したものを可視化したい場合は、Python で事前に処理して D3.js に渡した方が軽快かも


 

参考本

サンプルが多くて初心者向け。

インタラクティブ・データビジュアライゼーション ―D3.jsによるデータの可視化

インタラクティブ・データビジュアライゼーション ―D3.jsによるデータの可視化

現場ですぐ使える時系列データ分析 ~データサイエンティストのための基礎知識~

現場ですぐ使える時系列データ分析 ~データサイエンティストのための基礎知識~

本番運用しているブログサイトの Mezzanine を 4.1.0 から 4.2.2 にアップデートしてみた

Monotalk さんの以下の記事に触発されて、私が本番運用している某ブログサイトの Mezzanine を 4.1.0 から 4.2.2 にアップデートしてみました。


アップデートするのに結構苦労したように書いてあったので、念のため、Vagrant の開発環境と EC2 のバックアップイメージから起動したインスタンス上でのリハーサルをしてから臨みました。


結論から言うと、あまり苦労せずにアップデートできました。Monotalk さんは Mezzanine 3 系から運用していたような感じだったので、そこからの負債が溜まっていたのではないかと推測します。


 

アップデート手順

アップデートの手順はたったこれだけです。

$ pip install -U Mezzanine==4.2.2

$ python manage.py makemigrations
$ python manage.py migrate



アップデート前の本番環境のインストール済みライブラリ一覧(2016/11/2 時点)

$ pip list
awscli (1.11.10)
beautifulsoup4 (4.5.1)
bleach (1.4.3)
boto (2.41.0)
botocore (1.4.67)
chardet (2.3.0)
colorama (0.3.7)
Django (1.9.9)
django-appconf (1.0.2)
django-compressor (2.0)
django-contrib-comments (1.7.2)
django-ses (0.8.0)
docutils (0.12)
filebrowser-safe (0.4.5)
future (0.15.2)
futures (3.0.5)
grappelli-safe (0.4.4)
gunicorn (19.6.0)
html5lib (0.9999999)
jmespath (0.9.0)
Mezzanine (4.1.0)
MySQL-python (1.2.5)
oauthlib (1.1.2)
Pillow (3.3.1)
pip (8.1.2)
psycopg2 (2.6.2)
pyasn1 (0.1.9)
python-dateutil (2.5.3)
python-memcached (1.58)
pytz (2016.6.1)
rcssmin (1.0.6)
requests (2.11.1)
requests-oauthlib (0.6.2)
rjsmin (1.0.12)
rsa (3.4.2)
s3transfer (0.1.9)
setproctitle (1.1.10)
setuptools (24.0.2)
six (1.10.0)
tzlocal (1.2.2)
wheel (0.29.0)


アップデート後のインストール済みライブラリの一覧は、以下のようになりました。

$ pip list
awscli (1.11.10)
beautifulsoup4 (4.5.1)
bleach (1.4.3)
boto (2.41.0)
botocore (1.4.67)
chardet (2.3.0)
colorama (0.3.7)
Django (1.10.3)
django-appconf (1.0.2)
django-compressor (2.0)
django-contrib-comments (1.7.3)
django-ses (0.8.0)
docutils (0.12)
filebrowser-safe (0.4.6)
future (0.16.0)
futures (3.0.5)
grappelli-safe (0.4.5)
gunicorn (19.6.0)
html5lib (0.9999999)
jmespath (0.9.0)
Mezzanine (4.2.2)
MySQL-python (1.2.5)
oauthlib (2.0.0)
Pillow (3.4.2)
pip (8.1.2)
psycopg2 (2.6.2)
pyasn1 (0.1.9)
python-dateutil (2.5.3)
python-memcached (1.58)
pytz (2016.7)
rcssmin (1.0.6)
requests (2.11.1)
requests-oauthlib (0.7.0)
rjsmin (1.0.12)
rsa (3.4.2)
s3transfer (0.1.9)
setproctitle (1.1.10)
setuptools (24.0.2)
six (1.10.0)
tzlocal (1.3)
wheel (0.29.0)


Mezzanine 4.1.0 から 4.2.2 への一番の変更点は、Django のバージョンが 1.9 系から 1.10 系にアップデートされることでしょうか。

4.1.0 から 4.2.2 への全ての変更箇所は以下で確認することができます。
https://github.com/stephenmcd/mezzanine/compare/4.1.0...4.2.2


このアップデートでいくつかのバグ(*1)が本家で修正されたのに伴い、技術的な負債を少し解消することができました。



 

影響

4.2.2 へのアップデート後、ping_google コマンド実行時にエラーが出るようになってしまいました。

$ python manage.py ping_google
Traceback (most recent call last):
  File "manage.py", line 14, in <module>
    execute_from_command_line(sys.argv)
  File "/home/vagrant/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 367, in execute_from_command_line
    utility.execute()
  File "/home/vagrant/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 359, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/vagrant/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/core/management/base.py", line 294, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/vagrant/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/core/management/base.py", line 345, in execute
    output = self.handle(*args, **options)
  File "/home/vagrant/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/contrib/sitemaps/management/commands/ping_google.py", line 12, in handle
    ping_google(sitemap_url=options['sitemap_url'])
  File "/home/vagrant/.virtualenvs/akiyokoproject/local/lib/python2.7/site-packages/django/contrib/sitemaps/__init__.py", line 36, in ping_google
    raise SitemapNotFound("You didn't provide a sitemap_url, and the sitemap URL couldn't be auto-detected.")
django.contrib.sitemaps.SitemapNotFound: You didn't provide a sitemap_url, and the sitemap URL couldn't be auto-detected.

いろいろ調査したのですが、まだ解決していません。。

Django 本体の差分にも原因があるのかなぁ??
https://fossies.org/diffs/Django/1.9.8_vs_1.10/django/contrib/sitemaps/__init__.py-diff.html



 

バックアップイメージでの検証

EC2 のバックアップイメージから起動したインスタンス上でのリハーサル手順をメモしておきます。

1. バックアップイメージからインスタンス起動

まずは、EC2 インスタンスの Create Image をおこない、AMI を作成します。

作成したイメージからインスタンスを起動し、新たな Elastic IP を付与します。


 

2. settings.py の修正

config/local_settings.py

ALLOWED_HOSTS = ['akiyoko.com']

を、

ALLOWED_HOSTS = ['52.199.xx.xx']

と修正します(新しい IP アドレスを「52.199.xx.xx」と想定)。


最後に、プロセスを再起動。

$ sudo supervisorctl restart all


なおこれを修正しないと、アクセス時に Bad Request (400) が発生してしまいます。

(参考)django - Bad request 400: nginx / gunicorn - Stack Overflow

 

3. Nginx の設定ファイル修正

server_name に指定しているドメインを、Elastic IP に変更します。
また、テスト検証では HTTPS は使用しないので、SSL の設定は取り除きます。


/etc/nginx/sites-enabled/akiyokoproject.conf

(変更前)

server {

    #listen 80;
     listen 443 ssl;
    server_name akiyoko.com;
    client_max_body_size 10M;
    keepalive_timeout    15;
    error_log /home/webapp/logs/akiyokoproject_error_nginx.log info;

     ssl_certificate      conf/akiyokoproject.crt;
     ssl_certificate_key  conf/akiyokoproject.key;
     ssl_session_cache    shared:SSL:10m;
     ssl_session_timeout  10m;
     ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
     ssl_prefer_server_ciphers on;

    # Deny illegal Host headers
    if ($host !~* ^(akiyoko.com)$) {
        return 444;
    }
    ・
    ・

server {
    listen 80;
    server_name akiyoko.com;
    return 301 https://akiyoko.com$request_uri;
}

server {
    listen 80;
    listen 443 ssl;
    server_name www.akiyoko.com;
    return 301 https://akiyoko.com$request_uri;
}


(変更後)

server {

    listen 80;
    # listen 443 ssl;
    server_name 52.199.xx.xx;
    client_max_body_size 10M;
    keepalive_timeout    15;
    error_log /home/webapp/logs/akiyokoproject_error_nginx.log info;

     #ssl_certificate      conf/akiyokoproject.crt;
     #ssl_certificate_key  conf/akiyokoproject.key;
     #ssl_session_cache    shared:SSL:10m;
     #ssl_session_timeout  10m;
     #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
     #ssl_prefer_server_ciphers on;

    # Deny illegal Host headers
    if ($host !~* ^(52.199.xx.xx)$) {
        return 444;
    }
    ・
    ・

#server {
#    listen 80;
#    server_name akiyoko.com;
#    return 301 https://akiyoko.com$request_uri;
#}

#server {
#    listen 80;
#    listen 443 ssl;
#    server_name www.akiyoko.com;
#    return 301 https://akiyoko.com$request_uri;
#}


最後に Nginx をリロード。

$ sudo service nginx reload


 

4. Mezzanine アップデート

$ workon akiyokoproject
(akiyokoproject)$ pip install -U Mezzanine==4.2.2
  ・
  ・
Successfully installed Mezzanine-4.2.2 django-1.10.3 django-contrib-comments-1.7.3 filebrowser-safe-0.4.6 future-0.16.0 grappelli-safe-0.4.5 oauthlib-2.0.0 pillow-3.4.2 pytz-2016.7 requests-oauthlib-0.7.0 tzlocal-1.3
(akiyokoproject)$ python manage.py makemigrations
(akiyokoproject)$ python manage.py migrate

Mezzanine 系のモデル変更はありませんでしたが、Django 系の auth_user のモデルが少し変更になったようです。

$ sudo supervisorctl restart all


 

5. テンプレートの更新

HTMLファイル確認用の templats ディレクトリに、テンプレートの更新差分(4.1.0 → 4.2.2)をマージします。

config/settings.py

@@ -273,7 +275,7 @@ TEMPLATES = [
     {
         "BACKEND": "django.template.backends.django.DjangoTemplates",
         "DIRS": [
-            os.path.join(PROJECT_ROOT, "custom/templates"),
+            #os.path.join(PROJECT_ROOT, "custom/templates"),
             os.path.join(PROJECT_ROOT, "templates")
         ],
         "APP_DIRS": True,

上記のように修正して、一時的にカスタムテンプレートを外してから、

$ python manage.py collecttemplates

を実行して、テンプレートを上書きします。

なお、上書きするときにいちいちオーバーライドするかどうか聞かれるので、
~/.virtualenvs/akiyokoproject/lib/python2.7/site-packages/mezzanine/core/management/commands/collecttemplates.py

               self.stdout.write("Template exists%s.\n" % prev)
                #confirm = input("Overwrite?  (yes/no/abort): ")
                #while confirm not in ("yes", "no", "abort"):
                #    confirm = input(
                #        "Please enter either 'yes', 'no' or 'abort': ")
                #if confirm == "abort":
                #    self.stdout.write("Aborted\n")
                #    break  # exit templates copying loop
                #elif confirm == "no":
                #    self.stdout.write("[Skipped]\n")
                #    copy = False
            if copy:
                try:
                    os.makedirs(os.path.dirname(dest))
                except OSError:
                    pass
                shutil.copy2(path, dest)
                template_src[name] = app
                count += 1
        if verbosity >= 1:
            s = "s" if count != 1 else ""
            self.stdout.write("\nCopied %s template%s\n" % (count, s))

とコメントアウトすると、手間が省けます。


custom アプリケーションで独自に修正したテンプレートの一覧は以下の通りなので、

custom/__init__.py
custom/static/css/custom.css
custom/static/img/slide-1.jpg
custom/static/img/slide-2.jpg
custom/templates/base.html
custom/templates/blog/blog_post_detail.html
custom/templates/blog/blog_post_list.html
custom/templates/generic/includes/comment.html
custom/templates/generic/includes/comments.html
custom/templates/includes/form_fields.html
custom/templates/index.html
custom/templates/pages/menus/dropdown.html
custom/templates/twitter/tweets.html
custom/templatetags/__init__.py
custom/templatetags/add_attributes.py

本家テンプレートの更新差分(4.1.0 → 4.2.2)と重複したファイル

  • blog/blog_post_detail.html

については手動でマージをおこない、その他の

  • includes/editable_loader.html
  • pages/form.html
  • pages/menus/admin.html

については custom アプリケーションの templates ディレクトリ以下のものに上書きしました。