Django 1.4 で新しくプロジェクトを作成した際にしておくsettings.pyの設定

django 1.4 からはプロジェクトを作成した際のディレクトリ構成が変わったのでどのように設定するのがいいかを、まだ数の少ないネット上のサンプルを参考にしつつまとめてみた。

ちなみに以下の様な構成を想定してます。

└── project_root
    ├── app1
    │   ├── __init__.py
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    ├── app2
    │   ├── __init__.py
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    ├── manage.py
    └── package_root
        ├── __init__.py
        ├── settings.py
        ├── site_media
        │   ├── media
        │   └── static
        ├── static
        ├── templates
        ├── urls.py
        └── wsgi.py

settings.pyを編集

序盤は公式ドキュメントのチュートリアルにも出てくるような内容だけど一応。path周りが今回のメインだけど、正直この構成がベストプラクティスなのかはわからない。

データベース設定
# とりあえず冒頭で os モジュールをインポートしておく
import os

# 中略

# データベースの設定。とりあえずsqlite3。
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'example.sqlite',
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
    }
}
タイムゾーン言語コードの設定を変更
TIME_ZONE = 'Asia/Tokyo'
LANGUAGE_CODE = 'ja'
各種Pathの指定
# プロジェクトのルート。
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
# パッケージのルート(settings.pyの入っているディレクトリ)
PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__))

# ユーザーからアップロードされた画像などを保存する場所
MEDIA_ROOT = os.path.join(PACKAGE_ROOT, 'site_media', 'media')
MEDIA_URL = '/site_media/media/'
# js/css/imgなどの静的ファイルの保存場所
STATIC_ROOT = os.path.join(PACKAGE_ROOT, 'site_media', 'static')
STATIC_URL = '/site_media/static/'
# 特定のアプリと関係のないstaticファイルの格納場所
STATICFILES_DIRS = (
    os.path.join(PACKAGE_ROOT, 'static'),
)
# テンプレートファイルの格納場所
TEMPLATE_DIRS = (
    os.path.join(PACKAGE_ROOT, 'templates'),
)

os.path関連の解説

os.path周りのちょっとした解説をしておきます。ほんとちょっとだけ。__file__ == /home/kk6/hoge.py だとします。

絶対パスの表示
os.path.abspath(__file__)  # /home/kk6/hoge.py
ファイルのディレクトリの表示
os.path.dirname(__file__)  # /home/kk6
1階層上のディレクトリを表す
os.pardir  # '..'
カレントディレクトリ
os.curdir  # '.'
ディレクトリ間のセパレータ
os.sep  # '/'
ファイル名と拡張子のセパレータ
os.extsep  # '.'

以上を踏まえた上で

# これだとなんかモヤモヤするので
os.path.join(os.path.dirname(__file__), os.pardir)  # /home/kk6/..

# 絶対パスにする
os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)  # /home

結局...

os モジュールをしっかり理解すればそれで済むことに気づいた。

デコレータを作ってみる

前回の記事(Pythonのデコレータを理解するときに残したメモ - kk6のメモ帳*)でデコレータについて理解したので、じゃあ今度はデコレータを作ってみようと思う。あんまり詳しくないけどflaskを題材としてみる。

# hello.py
from flask import Flask
from flask import render_template

app = Flask(__name__)

@app.route('/<name>')
def hello(name):
    return render_template('hello.html', name=name)

if __name__ == '__main__':
    app.run(debug=True)

htmlはなんか適当に下みたいな感じに用意してください。

<!DOCTYPE HTML>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Hello Flask</title>
</head>
<body>
  {% if name %}
    <h1>Hello {{ name }}!</h1>
  {% else %}
    <h1>Hello World!</h1>
  {% endif %}
</body>
</html>

構成はこんな感じ

├── hello.py
└── templates
     └── hello.html

flaskのview関数って基本的に return render_template('hoge.html', hoge=hoge)とかすると思うんだけど、それは面倒なのでview関数はシンプルにテンプレートに渡すコンテキストの辞書を返すようにして、その辞書とテンプレートを受け取ってrender_templateを実行するデコレータを作ってみる。

具体的にはこんな感じになるように実装する。

@app.route('/<name>')
@render('hello.html')
def hello(name):
    return {
        'name': name,
    }

考え方というか説明の仕方として二通り思いついたんだけど、どっちがわかりやすいか判断つかなかったので一応両方掲載。個人的には後者のほうがわかりやすいような気もする。

考え方その1:内側から組み立てる

まず、最終的に実行したいものを明確にする。view関数が返す辞書をrender_templateに渡して実行したい。

ctx = view_func(*args, **kw)
render_template(template, **ctx)

まずはこれを関数にする

def wrapped(*args, **kw)
    ctx = view_func(*args, **kw)
    return render_template(template, **ctx)

この時点で足りないのはview_funcとtemplate。view_funcはデコレート対象の関数。templateはデコレータ自身に渡す引数。なのでまずはview関数を引数に受取る関数で包む。(@render('hello.html')みたいな使い方のデコレータを作りたいわけなので、当然最も外側の関数名はrenderになるし、その引数はテンプレートのパスになる。必然的にview関数を受け取る関数はその内側にネストしてなくてはならなくなる)

def wrapper(view_func):
    @wraps(view_func)
    def wrapped(*args, **kw)
        ctx = view_func(*args, **kw)
        return render_template(template, **ctx)
    return wrapped

さらにデコレータ自身に渡す引数を受け取る関数で包む

def render(template)
    def wrapper(view_func):
        @wraps(view_func)
        def wrapped(*args, **kw)
            ctx = view_func(*args, **kw)
            return render_template(template, **ctx)
        return wrapped
    return wrapper

考え方その2:外側から組み立てる

まず、@render('hello.html')という使い方をしたいので、最も外側の関数は

def render(template):
    ....
    return ...

のようになる。続いて、その内部にview関数を受け取る関数wrapperを用意し、renderがwrapperを返すようにする。

def render(template):
    def wrapper(view_func):
        ...
        return ...
    return wrapper

さらに、元の関数の引数を受け取る関数wrappedを用意し、wrapperがwrappedを返すようにする。

def render(template):
    def wrapper(view_func):
        @wraps(view_func)
        def wrapped(*args, **kw):
            ...
            return ...
        return wrapped
    return wrapper

これでテンプレート(template)、view関数(view_func)、view関数の引数(*args, **kw)が揃ったので、あとはやりたい処理(render_templateの実行)を記述するだけ。

def render(template)
    def wrapper(view_func):
        @wraps(view_func)
        def wrapped(*args, **kw)
            ctx = view_func(*args, **kw)
            return render_template(template, **ctx)
        return wrapped
    return wrapper

ここからは余談

デコレータの作り方としては以上なんだけど、これだとview関数の戻り値が辞書じゃない場合(return redirect(url_for('hogehoge'))とかしてBase Responseとかが返ってくる場合)に、それをrende_templateに渡そうとしてしまってエラーになったりするので、辞書以外が来た場合はそのまま返すように手を加える。

def render(template)
    def wrapper(view_func):
        @wraps(view_func)
        def wrapped(*args, **kw)
            ctx = view_func(*args, **kw)
            if isinstance(ctx, dict):
                return render_template(template, **ctx)
            else:
                return ctx
        return wrapped
    return wrapper

これでひとまず使えるものができたと思います。flask詳しくないので何か足りないかもしれないけど、それはその都度拡張・修正ということで。

Pythonのデコレータを理解するときに残したメモ

Pythonのデコレータ(decorator)を理解する 3 - -を1,2,3全て読んで、以前僕がデコレータを理解するために残したメモもこんな感じだったなというのを思い出した。ずっと記事にしようと思いつつ忘れてたんだけどせっかくなので加筆修正しつつ公開してみる。

引数無しのデコレータ

デコレータとは、その名の通り元の関数を装飾するもの。別の言い方をすれば、元の関数そのものを弄ること無く挙動を変更するもの。

まずは単純なデコレータから。

def hello(function):
    def _hello(*args, **kw):
        result = function(*args, **kw)
        return "Hello, {0}".format(result)
    return _hello

@hello
def name(arg):
    """my docstring"""
    return arg

# これは以下と同義。実際デコレータ式が導入される前はこうしていた。
def name(arg):
    """my docstring"""
    return arg

name = hello(name)

# 実行
print name('John Doe')
  • デコレータ付ける前:name('John') # 'John'
  • デコレータ付けた後:name('John') # 'Hello, John'

元の関数nameは単純に与えられた引数をそのまま返すだけ。そこにデコレータhelloを付けることによって、"Hello, xxxx"のような文字列を返すように挙動が変更される。デコレータ式じゃない方を見てみるとわかるように、関数を返す関数helloの引数として関数nameを渡し、返ってきた関数をnameに再代入する感じ。

ところで、これだと元の関数名がデコレータの内側の関数名で上書きされてしまう。

name.func_name # '_hello'
name.func_doc  # None

これを解決するためには、functoolモジュールのwrapsを使う。

from functools import wraps

def hello(function):
    @wraps(function)
    def _hello(*args, **kw):
        return function(*args, **kw)
    return _hello

@hello
def name(arg):
    """my docstring"""
    return arg

これで以下のように関数名と関数のdocstringが正しく取得できるようになる

name.func_name # 'name'
name.func_doc  # 'my docstring'

デコレータ自体に引数を与える

flaskの簡単なview関数

@app.route('/')
def hello():
    return "Hello World!"

こんな風にデコレータ自体に引数を渡したい場合はどうするか。

この場合、

  • デコレート対象の関数の引数を受ける関数
  • デコレート対象の関数自身を引数として受け取る関数
  • デコレータ自身の引数を受け取る関数

が必要になるので、少なくとも3重にネストした関数になる。

from functools import wraps


def hello(friend1, friend2):
    def _hello(function):
        @wraps(function)
        def __hello(*args, **kw):
            result = function(*args, **kw)
            return "Hello, {0} & {1}! I'm {2}.".format(friend1, friend2, *args, **kw)
        return __hello
    return _hello


@hello("Jack", "Mary")
def name(arg):
    return arg


# デコレータ式を使わない場合
# 式の左側の変数名はイメージしやすいように_helloとかnameを使ったけれど別になんでもいい。
_hello = hello("Jack", "Mary")
name = _hello(name)


print name("John")  # 'Hello, Jack & Mary! I'm John.'