Hatena::ブログ(Diary)

SPEAKER BREAKA このページをアンテナに追加 RSSフィード

2011-10-04

factory_boy README 和訳

# 必要に迫られたので訳したよ。更新が久しぶりすぎて、はてダの使いかた忘れてる…




factory_boy は thoughtbot の factory_girl をベースにした fixture replacement です。 factory_girl のように、わかりやすい定義構文、 複数のビルドストラテジ(インスタンスの保存、非保存、属性辞書、スタブオブジェクト)の対応、 ファクトリの継承を含め、同一クラスに対する複数ファクトリの対応などがあります。 Django に対応していますが、他の ORM の対応も簡単に追加することができます。



クレジット



この README はできるだけ factory_girl の README に対応させています。 文章や例は比較しやすいように同じ内容にしています。 factory_girl を使っている Ruby ユーザーなら、すぐに Python の factory_boy を使えるでしょう。



factory_boy は Mark Sandstrom によって書かれました。



Joe Ferris と factory_girl を作ってくれた thoughtbot に感謝します。



ダウンロード

Github: http://github.com/dnerdy/factory_boy/tree/master

easy_install:

easy_install factory_boy

ソース:

# ソースをダウンロードして実行
python setup.py install


ファクトリの定義



ファクトリは、オブジェクトインスタンス化するのに使う属性のまとまりを宣言したものです。 ファクトリ名はデフォルトではオブジェクトのクラスを推測するのに使われますが、 明示的に指定することも可能です:

import factory
from models import User

# これは User クラスを推測します
class UserFactory(factory.Factory):
    first_name = 'John'
    last_name = 'Doe'
    admin = False

# これは(Admin クラスを推測しますが) User クラスを使います
class AdminFactory(factory.Factory):
    FACTORY_FOR = User

    first_name = 'Admin'
    last_name = 'User'
    admin = True


ファクトリの使用



factory_boy はいくつかの異なったビルドストラテジに対応しています: build, create, attributes そして stub です:

# save されていない User インスタンスを返します
user = UserFactory.build()

# save された User インスタンスを返します
user = UserFactory.create()

# User インスンタンスの生成に使える属性の辞書を返します
attributes = UserFactory.attributes()

# 全ての属性がスタブ化されたオブジェクトを返します(?)
stub = UserFactory.stub()


ファクトリクラスをデフォルトビルドストラテジのショートカットとして使うことができます:

# UserFactory.create() と同じ
user = UserFactory()


デフォルトのストラテジを上書きすることも可能です:

UserFactory.default_strategy = factory.BUILD_STRATEGY
user = UserFactory()


全ファクトリに対するデフォルトストラテジを上書きすることもできます:

# デフォルトのビルドストラテジが定義されていない全ファクトリのデフォルトストラテジを設定
factory.Factory.default_strategy = factory.BUILD_STRATEGY


どのストラテジが使われていても、 キーワード引数を渡すことで既に定義済の属性を上書きすることが可能です:

# User インスタンスを生成して first_name を上書きする
user = UserFactory.build(first_name='Joe')
user.first_name
# => 'Joe'


遅延評価属性



ほとんどのファクトリ属性は、ファクトリを定義する際に評価される 静的な値を使ってファクトリに追加することができますが、 いくつかの属性(関連や動的に生成されるべき属性など)は インスタンスが生成される度に値が設定される必要があります。 そのような "lazy" な属性は、次のように追加することができます:

class UserFactory(factory.Factory):
    first_name = 'Joe'
    last_name = 'Blow'
    email = factory.LazyAttribute(lambda a: '{0}.{1}@example.com'.format(a.first_name, a.last_name).lower())

UserFactory().email
# => 'joe.blow@example.com'


LazyAttribute に渡された関数には、その宣言までにファクトリに定義された属性が与えられます。



ラムダ式では具合が悪い場合は lazy_attribute デコレータ関数を装飾することができます:

# Stub ファクトリは特定のクラスとのひもづきを持たない
class SumFactory(factory.StubFactory):
    lhs = 1
    rhs = 1

    @lazy_attribute
    def sum(a):
        result = a.lhs + a.rhs  # などのなんらかの計算
        return result


関連



LazyAttribute を使うことで、ひもづいたオブジェクトインスタンスを生成することもできます:

from models import Post

class PostFactory(factory.Factory):
    author = factory.LazyAttribute(lambda a: UserFactory())


ひもづいたオブジェクトでは、常にデフォルトストラテジが使われます:

# User と Post をビルドして保存する
post = PostFactory()
post.id == None           # => False
post.author.id == None    # => False

# User をビルドして保存してから Post をビルドするが保存はしない
post = PostFactory.build()
post.id == None           # => True
post.author.id == None    # => False


継承



継承を使えば、何度も共通の属性の宣言をしなくても、簡単に同じクラスのファクトリを複数作ることができます:

class PostFactory(factory.Factory):
    title = 'A title'

class ApprovedPost(PostFactory):
    approved = True
    approver = factory.LazyAttribute(lambda a: UserFactory())


シーケンス



特定のフォーマット(Eメールアドレスなど)のユニークな値はシーケンスを使って生成できます。 シーケンスは Sequence で定義するか sequence デコレータを使います:

class UserFactory(factory.Factory):
    email = factory.Sequence(lambda n: 'person{0}@example.com'.format(n))

UserFactory().email  # => 'person0@example.com'
UserFactory().email  # => 'person1@example.com'


シーケンスは遅延属性と組み合わせて使うこともできます:

class UserFactory(factory.Factory):
    name = 'Mark'
    email = factory.LazyAttributeSequence(lambda a, n: '{0}+{1}@example.com'.format(a.name, n).lower())

UserFactory().email  # => mark+0@example.com

2010-04-12

Django クエリセットの defer / only メソッド 直訳

Django 1.2 目前ですが、1.1 からクエリセットのメソッドに、クエリ最適化に利用できる defer()only() メソッドが追加されています。


no title


まあいつものように、自分のために直訳。


defer(*fields)

複雑なデータモデリングが必要な状況では、モデルに大量のフィールドを持っていたり、 フィールドに(テキストフィールドなどで)大量のデータを持っていたり、 Python オブジェクトに変換するのに重い処理が必要だったりするだろう。 クエリセットの結果のうち、必要のないフィールドがわかっている場合、 Django にそれらのフィールドをデータベースから取得しないように指定することができる。


これは defer() にロードしないフィールド名を渡せばいい:

Entry.objects.defer("lede", "body")

この遅延クエリセットもまたモデルインスタンスを返す。 defer で指定された各フィールドは、そのフィールドにアクセスした時にデータベースから取得される。 (一度に全ての遅延フィールドを取得するのではなく、1フィールドだけ)。


defer() の複数呼び出しも可能。 各呼び出しは新しいフィールドを遅延セットに追加する:

# body と lede フィールドの取得を延期する
Entry.objects.defer("body").filter(headline="Lennon").defer("lede")

遅延セットへの追加順は関係ない。 既に遅延指定されたフィールド名の defer() 再呼び出しも問題ない(そのフィールドは遅延指定されたまま)。


(select_related() でロードされていれば)関連モデルのフィールドに対する遅延ロードも、 関連フィールドを標準的な二重アンダースコアの記述する方法で指定可能:

Blog.objects.select_related().defer("entry__lede", "entry__body")

遅延フィールドの解除は defer()パラメータとして None を渡せばいい:

# 遅延せずに全フィールドを読み込む
my_queryset.defer(None)

いくつかのフィールドは、指定したとしても遅延処理されない。 主キーは決して遅延読み込みはできない。 select_related() を使って同時に他のモデルを取得する場合、 主モデルから接続される関連モデルフィールドの遅延読み込みをするべきではない (その場ではエラーにはならないが、結果としてエラーが発生する)。


note:

defer() メソッド(と兄弟の only() メソッド)は、高度なユースケースのために存在している。これらはクエリ分析時の最適化や、どの情報が必要かを 厳密に 把握している場合や、 モデルにおいて、必要なフィールドだけ読み込んだ場合と全てのフィールドを返した場合で大きな違いがあることが測定されている場合のために提供されている。 アプリケーション開発の初期の段階から、わざわざ defer() を使う必要はない; クエリ設計が落ち着いて、どこが問題点なのか理解できるまで触るべきではない。


only(*fields)

only() メソッドは defer() メソッドとほぼ反対のメソッドだ。 モデルを取得する際に、遅延取得 しない フィールドを指定して呼び出す。 ほとんどのフィールドを遅延読み込みするようなモデルの場合に、コードを簡潔にするために 遅延処理しないフィールドを指定するために only() を使用する。


name, age, biography フィールドを持つモデルの場合、次の2つのクエリセットは 遅延フィールドとして同じものになる:

Person.objects.defer("age", "biography")
Person.objects.only("name")

only() 呼び出しはいつでも、即時ロードするフィールドセットを 置換 する。 このメソッド名は覚えやすい用につけたもので: 指定したフィールド だけ 即時ロードされ; 残りのフィールドは遅延ロードされる。 このため、連続した only() 呼び出しは、最後の指定されたフィールドだけが考慮される:

# これは headline 以外のフィールドが遅延処理される
Entry.objects.only("body", "lede").only("headline")

defer() は追加処理される(遅延処理リストにフィールドを追加していく)ため、 only()defer() の結合呼び出しが可能であり、これらは論理的にふるまう:

# 最終的に "headline" 以外が遅延処理される。
Entry.objects.only("headline", "body").defer("body")

# 最終的に headline と body が即時処理される
# (only() が全フィールドセットを置換するため)
Entry.objects.defer("body").only("headline", "body")

2010-04-06

Jogging README 翻訳

Django アプリのログを取りたい場合に Jogging が便利そうなので、いつもの直訳です。


Jogging README

Jogging は Django でのロギングを簡単にするための、Python の logging の薄いラッパです。 これはすべてのロガーの設定を一箇所でできるようにし、ロガーをインポートする標準の場所の提供や、一般的なロギングユースケースを簡潔にすることができます。


Jogging を使えば、保存場所やフォーマット、適切な粒度の詳細ログの管理が可能です。 モジュールレベルのロギング設定も、特定の関数のロギング設定のように簡単に実現できます。


使用するには、settings.py に数行設定を追加し、Python のログ関数の変りに Jogging のログ関数をインポートします。そしていつものようにログを取るだけです。


Python の logging モジュールが全ての重要な処理を担っているため、Jogging から既に存在するコードのロギングの設定を行なうことができます。 また抽象化によって logging の威力が損なわれないようにしています。


ダウンロード

最新リリース版 (2009/10/27 リリースの v0.1)を ダウンロード セクションから取得できます。


インストール

  1. INSTALLED_APPS'jogging' を追加
  2. MIDDLEWARE_CLASSES'jogging.middleware.LoggingMiddleware' を追加

設定

言葉で説明するよりコードサンプルの方が簡単なので、いくつかのサンプルを載せます。 これらを settings.py に記述して下さい。


基本的な設定例
from jogging.handlers import DatabaseHandler
import logging

GLOBAL_LOG_LEVEL = logging.INFO
GLOBAL_LOG_HANDLERS = [DatabaseHandler()] # takes any Handler object that Python's logging takes

INFO レベル以上のログがデータベースへ保存されます。


Jogging はハンドラをラップしないことに注意して下さい; ハンドラは logging と同様のオブジェクトです。 これは logging.Hander でできることは全てできる、ということであり、Jogging にハンドラオブジェクトを渡すことができる、ということです。


中間的な設定例
from jogging.handlers import DatabaseHandler
from logging import StreamHandler
import logging

LOGGING = {
    # myapp1 の全てのログをデータベースに
    'myapp1': {
        'handler': DatabaseHandler(), # an initialized handler object
        'level': logging.DEBUG,
    },

    # ... この view だけは stderr に CRITICAL なログを吐く
    'myapp1.views.super_important_view': {
        'handler': StreamHandler(),
        'level': logging.CRITICAL,
    },
}

特定のロガーだけにマッチします(この例では super_important_view() ではデータベースにログを残しません)


高度な設定例
from jogging.handlers import DatabaseHandler
from logging import StreamHandler, FileHandler
import logging

LOGGING = {
    # myapp1 の全てのログをデータベースに
    'myapp1': {
        'handler': DatabaseHandler(),
        'level': logging.DEBUG,
    },

    # 今回も super_important_view だけ CRITICAL なログを stderr に吐くが、
    # それ以外のレベルのログはデータベースに吐く
    'myapp1.views.super_important_view': {
        'handlers': [
            { 'handler': StreamHandler(), 'level': logging.CRITICAL,
                'format': "%(asctime)-15s %(source): %(message)s %(foo)s" },
            { 'handler': DatabaseHandler(), 'level': logging.DEBUG },
        ]
    },

    # サードパーティのアプリにおいて、既にログをとっている場合のロガー名。
    # これも他の設定と同様に記述できる。
    'simple_example': {
        'handler': StreamHandler(),
        'level': logging.CRITICAL,
    }
}

ハンドラのフォーマットプロパティは Python の logging の書式化文字列に加え、以下の拡張文字列があります:

  • %(source)s はロギング呼び出し元のメソッド
  • %(foo)s はロギング呼び出し時に渡されたパラメータ

Usage

from jogging import logging
logging.info("I'm an info message")
logging.debug(msg="I'm a debug message", foo="bar")

%(foo)s は上記の高度な例で言及した 'format' プロパティにあったということを覚えてるでしょうか? これはデバッグ呼び出し時には "bar" として与えられます。


カスタムハンドラ

jogging.handlers.DatabaseHandler
データベースにログを保存し、管理画面において閲覧/検索/絞り込みが可能になります。
jogging.handlers.EmailHandler
ログをEメールで送信します。
jogging.handlers.InlineOnPageHandler
作成予定。レンダーしたページの下部にログを表示します。

FAQ

Jogging と django-logging の違いは?
Djagno logging は単一のルートロガーを提供しますが、Jogging は異なるモジュールで異なるロガーを使うことができます。 上述の "基本的な設定例" のように設定することで、django-logging の同じような使い方ができます。
Jogging と django-db-log の違いは?
django-db-log は例外のログをデータベースに保存してくれるだけです。 これはデバッグや一般的なロギングの目的には合っていませんし、Python の logging モジュールで提供するような機能は何もありません。 Jogging は DatabaseHandler というハンドラを使えば、django-db-log のように例外ログ(だけでなくなんでも)データベースへ保存することができます。
logging の log 関数と Jogging を併用できるか?また Jogging の log 関数を使ったほうがいい理由は?
二つの理由があります: まずひとつめは、ロガーのフォーマッタにおいて、呼び出し関数名として source 変数を使うことができます。 ふたつめは、Jogging の log 関数は自動的に正しいロガーを選択するため、どのロガーが設定されているのか気にする必要がありません。

実装について

Django’s logging proposal から多大なインスピレーションを受けています。


Jogging は Jogging によって管理するロガーの(名前の)定義辞書 settings.LOGGING の設定が必要です。Jogging の動作は:

  1. 全てのロガーはサーバの起動時に settings.LOGGING から作成されます (初期化コードは、ちょうどいい場所がなかったので、ミドルウェア内の __init__ にしています)。 ハンドラは定義通りにロガーに追加され、ログレベルがセットされます。
  2. アプリケーションが Jogging の log 関数を呼び出す際、呼び出し元の関数settings.LOGGING で設定されたロガー名から 合ったものを探し、より具体的なロガー名が選択されます。 例えば、呼び出し元を myproj.myapp.views.func() とした場合、次の名前のロガーからマッチするロガー名を探します。 myproj.myapp.views.func, myproj.myapp.views, myproj.myapp, myproj 。 この場合、最初の(そしてより具体的な)ロガーがマッチして選択されます。
  3. log() は選択されたロガーから呼び出され、Python の logging モジュールに渡されます。

リソース

Python logging モジュールのハンドラ一覧:

http://docs.python.org/library/logging.html#handler-objects

Python loggign モジュールの書式化文字列:

http://docs.python.org/library/logging.html#formatter-objects


ToDo

  • Instantiate handlers outside of settings.py (e.g. so the ORM can be used)
  • settings.py より外でハンドラのインスタンス化を行なうようにする(例えば ORM で使えるように)
  • もっとカスタムハンドラを作成する

名前について

最初の "j" は発音せずに "ヨギング" と読みます。

2010-03-16

再利用可能なDjangoアプリ開発 超訳

古いけど、これまたいい内容なので勝手に翻訳します。誤訳があれば是非ご指摘を。 しかもスライド資料の翻訳なので、流れがわかりづらい場合は是非、元動画を見ながらどうぞ。

DjangoCon 2008 Reusable Apps
http://www.youtube.com/watch?v=A-S0tqpPga4
PDF
http://media.b-list.org/presentations/2008/pycon/reusable_apps.pdf

4つの道筋

  • ひとつのことをうまくやれ
  • 複数アプリにすることを恐れるな
  • 柔軟さを考慮して作れ
  • 配布のことを考えて作れ

その1 「ひとつのことをうまくやれ」 -- UNIX 哲学

アプリケーション == カプセル化

ケーススタディ:ユーザー登録
仕様
  • ユーザーがフォームから登録すると有効化されていないアカウントが作成される
  • ユーザーがリンクを記載したメールを受信し、それをクリックして有効化する
  • それだけ

いくつかの「シンプル」なことはそんなにシンプルじゃない。

仕様を疑ってかかる

この仕様を追加するべきか?
  • このアプリケーションがすべきことは何?
  • この仕様はそれに関係があるのか?
  • 無い?なら追加すべきじゃないと思うよ。

django-registeration の一番の要求仕様: ユーザープロフィールだ

「ユーザー登録になければいけないことはなにか?」 -- 私

"No, You Can't Have a Pony"

じゃあ解決法は何?

django-profiles
  • プロフィールの追加
  • プロフィールの編集
  • プロフィールの閲覧
  • それだけ

その2 複数アプリにすることを恐れるな

一枚岩な考え方
Django的な考え方
この機能はこのアプリケーションが持つべきか?
  • この機能は他の部分とは関係が無いか?
  • 似たような機能が他のサイトでも必要になるだろうか?
  • イエス?なら別のアプリに切り出すべきだね
ケーススタディ:ブログ
ブログが欲しいなー
  • エントリとリンク
  • タギング
  • モデレート機能付きのコメント
  • コンタクトフォーム
  • 「About」ページ
  • などなどなど
ということで
  • blog アプリ(エントリとリンク)
  • サードパーティ製の tagging アプリ
  • contrib.comments + moderation アプリ
  • contact-form アプリ
  • contrib.flatpages
  • などなど
利点
  • 仕様を書き直さずにすむ(?)
  • 他のサイトにも簡単に使い回せる
urlpatterns += (‘’,
    (r’^contact/’, include(‘contact_form.urls’)),
)
でもこんな時はどうすれば?
サイト特有の要望
  • サイトAはメッセージを収集するだけのコンタクトフォームが欲しい
  • サイトBのマーケティング部はいろいろな情報が欲しい
  • サイトCは自動スパムフィルタに Akismet を使いたい

その3 柔軟さを考慮して作れ

良識として
  • 分別のあるデフォルト値を
  • 容易なオーバーライドを
  • 何事も不変な状態にしない
フォームの処理
  • form クラスを用意
  • だけど好きな form を使用できるようにする
class SomeForm(forms.Form):
  ...

def process_form(request, form_class=SomeForm):
  if request.method == ‘POST’:
    form = form_class(request.POST)
    ...
  else:
    form = form_class()
  ...
テンプレート
  • デフォルトテンプレートを指定する
  • だけど好きなテンプレートを使用できるようにする
def process_form(request, form_class=SomeForm,
                 template_name='do_form.html'):
  ...
  return render_to_response(template_name,
                            ...
フォームの処理
  • サブミット成功時の処理後にリダイレクトしたい場合
  • デフォルトのURLを用意する
  • だけど好きな URL を使用できるようにする
def process_form(request, form_class=SomeForm,
                 template_name='do_form.html',
                 success_url='/foo/'):
  ...
  return HttpResponseRedirect(success_url)
URL ベストプラクティス
  • アプリケーションに URLConf を持つ
  • 名前付きURLパターンを使用する
  • URLの逆引きを使用する: reverse(), permalink, {% url %}

その4 配布のことを考えて作れ

チュートリアルやったよね
  • from mysite.polls.models import Poll
  • mysite.polls.views.vote
  • include('mysite.polls.urls')
  • mysite.mysite.bork.bork.bork

プロジェクトとの結合は再利用性を低下させる

「プロジェクト」ってなんだ?
  • settings モジュール
  • ルート URLConf モジュール
  • それだけ
ljworld.com
  • worldonline.settings.ljworld
  • worldonline.urls.ljworld
  • そしてたくさんの再利用アプリ
再利用可能なアプリはこう見える
  • Python パス上のひとつのモジュールディレクトリ(registration, tagging, などなど)
  • パッケージの関連モジュール(ellington.events, ellington.podcasts, などなど)
  • プロジェクトを持たない
さて簡単
  • distutils か setuptools を使ってパッケージをビルドできるようにして
  • Cheese Shop(PyPI)に置く
  • みんながダウンロードしてインストールできる
一般的なベストプラクティス
  • 依存性について正直であるべし
  • 可能なら Python2.3 向けに実装すべし
  • release か trunck を使って、ドキュメントに書くべし(?)
  • trunk を使用するなら、頻繁にアップデートすべし
テンプレートは大変
  • テンプレートを提供することは、そのまま使えるので大きな利点になる
  • だけどテンプレートをポータブルに作るのは大変(ブロック構造や継承、タグライブラリなど)

自分の場合は、あんまりデフォルトテンプレートは用意しないね。

どちらにせよ
  • テンプレート名を記述するべし
  • テンプレートコンテクストを記述するべし
ドキュメンテーション狂になるべし
  • Python なんだから docstrings 書けるよ
  • Django でドキュメント生成するとか
  • これでユーザーはずっと君を愛してくれるよ
最後にもう一度
  • ひとつのことをうまくやれ
  • 複数アプリにすることを恐れるな
  • 柔軟さを考慮して作れ
  • 配布のことを考えて作れ
いい例
より詳細な情報

2010-02-01

Django 開発初心者に送る 10 の Tips (超訳?)

実践的な DJango テクニック集として、凄くいい記事だったので、勝手に超訳してみました。

http://zeroandone.posterous.com/top-10-tips-to-a-new-django-developer

1. import にプロジェクト名を書かないこと

例えば "project3" というプロジェクトに "xyz" アプリケーションがある場合、次のようにはしないこと。

  from project3.xyz.models import Author

これではプロジェクトとアプリケーションの結びつきが強すぎて、以下の弊害がおこる。

  1. アプリケーションの再利用がしづらい
  2. 将来プロジェクト名を変えたくなっても変更が難しい

なので、このようにしよう。

  from xyz.models import Author

python パス上にある django プロジェクトならば、このように書ける。

2. MEDIA_ROOT と TEMPLATE_DIRS をハードコードしないこと

settings.py において、MEDIA_ROOT と TEMPLATE_DIRS を次のように書いてはいけない。

   TEMPLATE_DIRS = ( "/home/html/project/templates",)
   MEDIA_ROOT = "/home/html/project/appmedia/"

これではプロジェクトを別サーバに移動したり、別OSに変更する際に書き換えなければならなくなる。


これらの問題は次のテクニックを使うことで、簡単に避けることができる。

  SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
  MEDIA_ROOT = os.path.join(SITE_ROOT, 'appmedia')
  TEMPLATE_DIRS = ( os.path.join(SITE_ROOT, 'templates'),)

自分はこのテクニックを Rob Hudson から学んだ。以下のリンクからより詳細な情報を得ることができる。

http://rob.cogit8.org/blog/2009/May/05/django-and-relativity-updated/

3. テンプレートに静的ファイルをハードコードしないこと

静的ファイル(JavaScript ファイルや css ファイル、画像など)のリンクを次のように書いてはいけない。

(MEDIA_URL を“/appmedia/”とする)

  <link rel="stylesheet" type="text/css" href="/appmedia/wysiwyg/jHtmlArea.css" />
  <script type="text/javascript" src="/appmedia/wysiwyg/jHtmlArea.js"></script>
  <script type="text/javascript" src="/appmedia/scripts/editor.js"></script>
  <script type="text/javascript" src="/appmedia/scripts/comments.js"></script>
  <script type="text/javascript" src="/appmedia/scripts/voting.js"></script>

上の書き方では、静的コンテンツを Amazon S3 を使って別サーバ(http://cdn.xyz.com としようか)に配置したい、なんていう場合に、全テンプレートの "/appmedia/" を http://cdn.xyz.com 書き換えなければいけなくなる。

うわぁ、超メンドくさい。


これはシンプルなテクニック、つまり "/appmedia/" のようなハードコーディングではなく、コンテクスト変数 {{MEDIA_URL}} を使うようにすることで、避けることができる

  <link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}wysiwyg/jHtmlArea.css" />
  <script type="text/javascript" src="{{ MEDIA_URL }}wysiwyg/jHtmlArea.js"></script>
  <script type="text/javascript" src="{{ MEDIA_URL }}scripts/editor.js"></script>
  
  <script type="text/javascript" src="{{ MEDIA_URL }}scripts/comments.js"></script>
  <script type="text/javascript" src="{{ MEDIA_URL }}scripts/voting.js"></script>

これで MEDIA_URL を "/appmedia/" から "http://cdn.xyz.com/" に変更すれば、全テンプレートに自動で反映される。いちいち全部のテンプレートを変更するなんていう超メンドくさいことはしなくていい。

どうやればコンテクスト変数を使えるの?

とっても簡単。RequestConext(request) を render_to_response に追加することで、便利なコンテクスト変数(ユーザーや取得ページなど)を、テンプレートに渡すことができる。

  return render_to_response("my_app/my_template.html", {'some_var': 'foo'},
  context_instance=RequestContext(request))

より詳細な情報

http://www.b-list.org/weblog/2006/jun/14/django-tips-template-context-processors/

4. コアビジネスロジックを views に書かないこと!

コアビジネスロジックを views に書く(例えば銀行 X に総額 P を追加して、銀行 Y から P を引く、なんていうロジック)のはよろしくない。


なんで?
  1. コードをユニットテストできない
  2. コードを再利用できない

じゃあどこに書けばいい?

models かヘルパー関数に書くほうがいい。


基本的なロジックはどうする?

(モデルの)オブジェクトを検索や取得してきたり、リストを渡したり…なんていう、本当に基本的なロジックならば views に書いてもいいと思う。

5. 本番サーバに適用するとき、DEBUG=False (settings.py) に変更するのは面倒

ローカル環境を公開環境に移すとき、DEBUG を False にするのをよく忘れてしまう(DEBUG = False にすると利点が多い)し、退屈な作業だ。


これは以下のように、ホスト名をチェックして DEBUG 変数を設定するようにすればいい。

  import socket
  
  if socket.gethostname() == 'productionserver.com':
    DEBUG = False
  else:
    DEBUG = True

このテクニックを最初に指摘したのは Jokull だ。


より詳細な情報は:

http://nicksergeant.com/blog/django/automatically-setting-debug-your-django-app-based-server-hostname


別のテクニックとして、ローカル環境では、(DEBUG=Trueに設定した)もうひとつの settings ファイルを使う方法もある。

  #Filename settings_debuy.py
  #It contains all variables from settings and it overrides the DEBUG variable to True
  #we use settings_debug.py to run the server locally python manage.py runserver settings=settings_debug.py
  
  from settings import *
  DEBUG = True
  
  #you can also add other things which helps you to do the debugging easily
  #like Debug toolbar etc...

より詳細な情報

http://blog.dpeepul.com/2009/07/02/from-now-you-will-never-forget-to-put-debug-true-in-django-production-environment/

6. サードパーティ製のアプリケーションに依存するテンプレートタグは一度だけ load する

サードパーティ製アプリケーションのテンプレートタグを使うには、次のようにテンプレート内で load する必要がある。

  {% load template_tags %}

サードパーティ製のテンプレートタグを利用するテンプレート全てに同様の記述をする必要があるが、これは DRY の原理に反する。これを改善しよう。

  from django import template
  template.add_to_builtins('project.app.templatetags.custom_tag_module')

このコードを、起動時に読み込まれるファイル(settings.py や urls.py、各アプリの models.py など)のどれかに書いておくだけ。


上記コードが起動時にテンプレートタグを読み込んでくれるので、 {% load template_tags %} を使わなくても、好きなテンプレートでそのテンプレートタグを使うことができる。

7. Urls.py

project/urls.py に全 URL を書かない

例えば

  urlpatterns = patterns('',
  url(r'^askalumini/question/$','.....registerInstitution',name='iregister'),
  url(r'^askalumin/answer/$','someview.....',name='newmemberurl'),
  url(r'^institution/member/$','someview.....',name="dashboardurl"),
  url(r'^institution/faculty/$','editInstitute',name="editinstituteurl"),
  url(r'^memeber/editprofile/$','editProfile',name="editprofileurl"),
  url(r'^member/changepassword/$','changePassword',name="changepasswordurl"),
  url(r'^member/forgotpassword/$','forgotPassword',name="forgotpasswordurl"),
  url(r'^member/changepicture/$','changePicture',name="changepictureurl"),
  url(r'^member/logout/$','memeberlogout',name="logouturl"), ,
  )

代わりに次のように、複数アプリケーションに分離する(プロジェクトの /urls.py に記述する)。

これによって、アプリケーションを変更作業なしに、異なるプロジェクトで再利用することができる。

  urlpatterns = patterns('',
  # Example:
  (r'^$', include('institution.urls')),
  (r'^institution/', include('institution.urls')),
  (r'^askalumini/', include('askalumini.urls')),
  (r'^member/', include('member.urls')),
  )

それから urls.py を各アプリケーションに作成し、対応する URL を追加する(以下は askalumini アプリを例として挙げている)。

  urlpatterns = patterns('askalumini.views',
  url(r'^$','askHome',name='askaluminiurl'),
  url(r'^questions/(?P<questionno>\d+)/$','displayQuestion',name='askquestiondisplay'),
  url(r'^askquestions/$','askQuestion',name='askquestionurl'),
  url(r'^postcomment/$','postComment',name="askquestioncomment")
URL の定義には url 関数を使用する

URL を定義するのに、URL に名前を付けられる url 関数を使用しているのに気付いたと思う。上で定義した全 URLには、最後に URL 名を指定している(name="....." のように)。この名前によって、views や templates、そして models において、URL のハードコーディングをしないで済むような、効果的な指定ができるようになる。


各アプリにおいて URL 名のユニークさを保つために、URL の命名は "<appname><somelabel>" のような命名規約に沿うようにしている(上のコード参照)。

URL をハードコードしない

なぜって?


urls.py の URL 構造を変更する場合、URL をハードコーディングした全ての箇所を書き換える必要が出てくるからだ。(超メンドくさい…)

views.py では

以下のように URL をハードコードする代わりに、

  HttpResponseRedirect("/askalumini/questions/54")

URL 名を用いて reverse ルックアップ関数を使用して URL を生成する。

  from django.core.urlresolvers import reverse
  HttpResponseRedirect(reverse('askquestiondisplay',kwargs={'questionno':q.id}))
models.py では

絶対 URL を生成するために、models.py では reverse ルックアップ関数に加え、models.permalink decorator を使用できる。

  @models.permalink
  def get_absolute_url(self):
    return ('profileurl2',(),{'userid': self.user.id})

このデコレータでも、URL を生成するために URL 名を使用する。

templates.py では

いろいろと不便なハードコーディグをする代わりに、 url タグ を使うことで、URL 名を用いて URL を生成することができる。

  {% url askquestiondisplay 345 %}
  <a href="{% url askquestiondisplay 345 %}"> Ask Question </a>

8. デバッグ

a. django-debug-toolbar を使うことで、以下のような情報を取得できる
  1. どれくらいの数の SQL 文が実行されたか?トータルタイムは?
  2. テンプレート名、ロギング、クッキー/セッション情報、などなど

debug-toolbar のより詳細な情報は以下のリンク先で確認できる。

http://github.com/robhudson/django-debug-toolbar/tree/master


b. Werkzeug デバッガを使えば、エラー画面で Python シェルを開くことができ、迅速なデバッグ作業に役立つ。

詳細な情報は以下のリンクから。

http://blog.dpeepul.com/2009/07/14/python-shell-right-on-the-django-error-page/


c. デバッグ作業に pdb を使う。

詳細はこちら。

http://ericholscher.com/blog/2008/aug/31/using-pdb-python-debugger-django-debugging-series-/

9. 可能なら利用できるように Pinax について知っておく

Django を使う最大の利点のひとつとして、アプリケーションの再利用性がある。

Django は他のフレームワークのような一枚岩な設計を志向せずに、再利用性を促進させている。これは、他のフレームワークではなく Django を選ぶ際の重要な理由のひとつであり、Pinax はそのような設計方針に対する成果のひとつだ。


Pinax って何?


近年では、世界のどのウェブサイトでも登録、OpenIDのサポート、グループ、ユーザープロフィールなどといった機能が要求される。ほとんどのサイトで、これらの機能のロジックを実装しなければならない。独立した再利用可能な機能を提供するプラットフォーム(django のプロジェクト)があり、またその上で開発してもらえれば、これらの機能は、様々なサイト間で再利用することができる。そのようなプラットフォームは、迅速なウェブサイト構築を可能にし、開発者は、彼らのサイト独自の部分の開発に注力することができる。


Pinax はそんな再利用可能なアプリケーションを集めた(Django の)プラットフォームだ。Pinax は次のような再利用可能な機能やアプリケーションを提供する。


  1. openid サポート
  2. (vCard, Google, Yahoo などからの)連絡先のインポート
  3. 通知フレームワークなどなど… より詳細な情報はこちら http://pinaxproject.com/

Pinax は James Tauber による強力なアイデアだ。可能なら是非使ってみてほしい。迅速なウェブアプリケーションの構築の助けになるはずだ。


より詳細な情報はこちら

http://uswaretech.com/blog/2009/03/create-a-new-social-networking-site-in-few-hours-using-pinax-platform-django/

10. 知っておくべきサードパーティ製アプリケーション

使うべきサードパーティ製アプリケーションがいくつかある。

1. マイグレーション

スキーマやデータのマイグレーションはどうしてる?


syncdb するよね。


最初はモデルの設計がデータベースに反映される。


でも何時間、何日か経ったら…


モデルにフィールドを追加したり、削除したり、設計を変更しなければならなくなる。

変更するよね。ここで、どうやってこの変更をデータベースに反映させるかが重大な問題になる。

取れる方法としては、

  • もう一度 syncdb する(メンドくさい)
  • 手動で alter 文を DB に発行する(メンドくさい)

このようにデータベースをある状態から別の状態に変更することをマイグレーションと呼ぶ。このマイグレーションを楽にしてくれる、いくつかのサードパーティアプリケーションがある。

  • django-evolutions (簡単に使えて、自動で全部やってくれるけど、頑丈じゃない)
  • South (より強固だけど、ちょっと使い方を覚える必要がある)

2. テンプレート

Django のテンプレートは、制限がキツすぎると思ったら、次のようなものもある。

  1. template-utils (テンプレートタグやユーティリティよりも、テンプレートの機能を向上)
  2. Jinja (Django テンプレートと同じシンタックスを採用し、プロジェクトのプラグインとして使える、そしてより柔軟なロジック記述が特徴の、サードパーティのテンプレートシステム)

3. その他

1. django command extensions 次のようなお役立ちコマンドラインを提供してくれる

    1. 全モデルをロードしてくれる shell_plus
    2. Werkzeug デバッガを組み込んだ runserver_plus
    3. 上司に見せるためのモデル図生成

などなど。


より詳しい情報はこちら

http://ericholscher.com/blog/2008/sep/12/screencast-django-command-extensions/


2. Sorl はサムネイルを生成してくれる

など


他のリソースはこちら

  1. http://stackoverflow.com/questions/550632/favorite-django-tips-features