django.contrib.staticfilesを使う

しばらく仕事でDjangoをあんまり使ってなかったので、思い出しつつ新しい機能を勉強しつつ使ってます。
django.contrib.staticfilesはDjango1.3から使える機能。django-staticfilesというパッケージで公開されていたものがDjango本体に取り込まれた形。
https://docs.djangoproject.com/en/1.4/howto/static-files/
主な機能は2つ。

  • 静的ファイルを設定したいろいろな場所から読み込んでレスポンスを返します(主に開発時に使用)
  • 静的ファイルを一つのディレクトリに集めます(主にデプロイ時に使用)

使い方

使い方の説明。試したのはDjango1.4.1。

staticfilesを有効にする

django.contrib.staticfilesアプリケーションを有効にするにはsettingsのINSTALLED_APPSに追加します。Django1.4ではデフォルトで有効になっています。
settings.STATICFILES_FINDERSは、ファイルを読み込むクラスの設定です。FileSystemFinderとAppDirectoriesFinderがデフォルトで有効になっています。
FileSystemFinderはsettings.STATICFILES_DIRSに設定したディレクトリからファイルを読み込みます。
AppDirectoriesFinderは、各アプリケーションのstaticディレクトリからファイルを読み込みます。
settings.STATICFILES_STORAGEを設定することで、StorageBackendを切り替えることもできます。

staticfilesのディレクトリを設定する

アプリケーションと関係ない場所であればsettings.STATICFILES_DIRSにディレクトリを指定します。アプリケーションの下に置く場合はstaticという名前のディレクトリ下にファイルを配置します。
myproject/settings.py

# ...(略)
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # 'django.contrib.admin',
    # 'django.contrib.admindocs',
    'myapp',
)

import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))

STATIC_ROOT = os.path.join(BASE_DIR, 'deploy')  # プロジェクト直下のdeployディレクトリ
STATIC_URL = '/static/'  # 配信用のURL
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),  # プロジェクト直下のstaticディレクトリを指定
)
STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
)

# ...(略)

今回は、次のようにファイルを配置しました。

$ pwd
/home/tokibito/sandbox/myproject
$ tree
.
├── deploy
├── manage.py
├── myapp
│     ├── __init__.py
│     ├── models.py
│     ├── static
│     │     └── myapp-image.png
│     ├── tests.py
│     └── views.py
├── myproject
│     ├── __init__.py
│     ├── settings.py
│     ├── urls.py
│     └── wsgi.py
└── static
    └── project-image.png
runserverで動かしてみる

staticfilesが有効な場合、runserverはstaticfiles用のものに置き換わります。

$ python manage.py runserver

これで、 http://localhost:8000/static/project-image.pnghttp://localhost:8000/static/myapp-image.png のURLが有効になります。
DEBUGフラグが有効であれば、特に設定をしなくてもrunserverでstaticfiles配信のためのWSGIミドルウェアが差し込まれます。
runserverを使わない場合には次のurlpatternsの設定が必要です。(runserverを使う場合にurlpatternsを設定していても問題はありません)

開発用のurlpatternsを追加する

開発時にファイルを返すためのurlpatternsをプロジェクトのurls.pyに追加します。
myproject/urls.py

from django.conf.urls import patterns, include, url
from django.contrib.staticfiles.urls import staticfiles_urlpatterns

urlpatterns = patterns('',
    # ...(略)
)

urlpatterns += staticfiles_urlpatterns()

この時、staticfiles_urlpatternsを無効にしてしまうパターンが前に含まれないように注意する必要があります。すべてのURLを受け取るパターンを指定してる場合などは順番に特に注意。
また、staticfiles_urlpatternsはDEBUGフラグがTrueのときにしか動作しないことにも注意してください。

デプロイ用のファイルを集める

manage.pyのcollectstaticコマンドで、ファイルをSTATIC_ROOTディレクトリに集められます。
collectstaticコマンドは既存のファイルを上書きするため、確認の入力を求められます。yesと入力すると進みます。

$ python manage.py collectstatic

You have requested to collect static files at the destination
location as specified in your settings.

This will overwrite existing files!
Are you sure you want to do this?

Type 'yes' to continue, or 'no' to cancel: yes
Copying '/home/tokibito/sandbox/myproject/static/project-image.png'
Copying '/home/tokibito/sandbox/myproject/myapp/static/myapp-image.png'

2 static files copied.
$ tree
.
├── deploy
│     ├── myapp-image.png
│     └── project-image.png
├── manage.py
├── myapp
│     ├── __init__.py
│     ├── __init__.pyc
│     ├── models.py
│     ├── static
│     │     └── myapp-image.png
│     ├── tests.py
│     └── views.py
├── myproject
│     ├── __init__.py
│     ├── __init__.pyc
│     ├── settings.py
│     ├── settings.pyc
│     ├── urls.py
│     └── wsgi.py
└── static
    └── project-image.png

MEDIA_ROOT,MEDIA_URLとの違い

1.2以前のDjangoでは、アプリケーションの持つ静的ファイルと、ユーザーからアップロードされたファイルはMEDIA_ROOT,MEDIA_URLで一緒に管理されていました。
staticfilesと併用することで、これを分離できます。
ユーザーからアップロードされたファイルはMEDIA_URLで配信、アプリケーションの持つファイルはSTATIC_URLで配信といった形にできます。

django-staticfilesとの違い

Djagnoのドキュメントにも書かれていますが、django.contrib.staticfilesではなく、django-staticfilesを使うこともできます。django-staticfilesでは除外設定など一部設定が異なったり、クラス名が違ったりして動作も少し違います。

まとめ

staticfilesの使い方と注意点を紹介しました。アプリケーションを再利用する場合に特に有用な機能なので、是非使いこなしてみてください。
説明間違ってたらツッコミよろしく。

2012/9/24追記

テンプレートでの記述について書いてなかったので追記。
Djangoのテンプレートで静的ファイルのURLを指定する際には、STATIC_URLのコンテキストをプレフィックスとして指定します。

<img src="{{ STATIC_URL }}project-image.png" />

このコンテキストは、settings.TEMPLATE_CONTEXT_PROCESSORSのに設定されたdjango.core.context_processors.staticで供給されます。