cloverrose's blog

Python, Machine learning, Emacs, CI/CD, Webアプリなど

Django 静的ファイルに関するTips

Djangoの開発サーバーだけで静的ファイルを提供するときのまとめ
注意:公式サイトでも言われているように運用時はきちんとApacheなどを使うこと。

settings.pyでMEDIA_ROOTを設定

path/to/mysite/settings.pyのMEDIA_ROOTを次のように設定。
path/to/mediaの作成も忘れずに

import os
MEDIA_ROOT = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'media')

urls.pyで静的ファイルをdispatchする設定

site_media/icon.pngという要求に対して
path/to/media/icon.pngに保存されている画像を返す

from django.conf import settings
if settings.DEBUG:
    urlpatterns += patterns('',
        url(r'^site_media/(?P<path>.*)$', 'django.views.static.serve',
            dict(document_root=settings.MEDIA_ROOT)),
        )

静的ファイルユーティリティimage_utils.pyを作成

Webアプリでは
ローカルシステムの絶対パス相対パス
URLの絶対パス相対パスを上手く整理していく必要がある。
また、settings.pyで定義したMEDIA_ROOTや、urls.pyで静的ファイルのプレフィックスとしたsite_mediaが
あちこちに分散するのを防ぐため、次のようなユーティリティ関数群を定義する。

# -*- coding:utf-8 -*-

"""
静的ファイルに関するユーティリティ群
"""

import urllib
import os
from django.conf import settings


def get_img(url):
    """
    URLから画像データを取得
    imageファイルでない場合はNoneを返す
    """
    f = urllib.urlopen(url)
    file_type = f.info().gettype()
    src = f.read()  # source string (binary)
    f.close()
    if 'image' in file_type:  # not error page
        return src
    else:
        return None


def save_bindata(absolute_pathname, data):
    """
    絶対パスにバイナリデータを保存
    """
    out = open(absolute_pathname, 'wb')
    out.write(data)
    out.close()


def build_media_absolute_pathname(relative_pathname):
    """
    ローカルの相対パスにMEDIA_ROOTをつけてローカルの絶対パスにする
    """
    return os.path.join(settings.MEDIA_ROOT, relative_pathname)


def build_media_absolute_url(request, relative_pathname):
    """
    ローカルの相対パスからURLの絶対パスにする
    """
    relative_url = urllib.pathname2url(relative_pathname)
    # note: requestからprotocol,domain,portを考慮して完全URLを生成する
    return request.build_absolute_uri('/site_media/' + relative_url)

views.pyでの使い方

定義したユーティリティは以下の様な感じで利用する。

import os
from mysite.question.image_utils import get_img, save_bindata, build_media_absolute_pathname, build_media_absolute_url

image_url = 'http://www.python.org/images/python-logo.gif'
image = get_img(image_url)
    if image is not None:
        relative_pathname = os.path.join('langs', 'python.gif')
        absolute_pathname = build_media_absolute_pathname(relative_pathname)
        save_bindata(absolute_pathname, image)
    site_image_url = build_media_absolute_url(request, relative_pathname)

画像が保存されているかなどのテストは正規表現でチェック

>>> import re
>>> ptn = re.compile('http://.+/site_media/langs/(.+)')
>>> mo = ptn.match(site_image_url)
>>> mo is not None
True
>>> mo.group(1) == 'python.gif'
True

ポイント

request.build_absolute_uri