概要
Djangoにはファイルアップロードを簡単に実装する仕組みがあります。
FileField
、ImageField
というモデルフィールドがあり、これをモデルに定義するとモデルフォーム内にファイル選択欄が表示されます。アップロードされたファイルはサーバ内やAmazonS3やDropbox等の任意のストレージに保存することができます。
参考:https://docs.djangoproject.com/en/2.0/topics/http/file-uploads/
アップロードの流れ
1.モデルにFileField(ImageField)を定義すると
attach = models.FileField(
upload_to='uploads/%Y/%m/%d/',
verbose_name='添付ファイル',
validators=[FileExtensionValidator(['pdf', ])],
)
2.モデルフォームにファイル入力欄が追加されるので
3.データを登録するとMEDIA_ROOT
配下のupload_to
の場所にファイルが保存される
4.データベースには上記のパスが登録される
attach : uploads/2018/06/25/添付資料.pdf
5.http://ドメイン名 + MEDIA_URL + upload_to のパス
というURLでアップロードしたファイルにアクセスすることができる。
利用方法
とりあえず開発環境で動かすための設定になります。クラウドサービスを使う本番環境の設定はファイルアップロード機能の使い方 クラウドストレージ編 で説明します。
事前準備
FileField、ImageFieldでアップロードされるファイルを「メディアファイル」と呼びます。メディアファイルを扱うにはプロジェクト側でいくつかの設定が必要です。
1.保存場所の作成
メディアファイルを格納するディレクトリ(メディアルートと呼ぶ)が必要です。開発環境ではプロジェクトルート直下に**media
というディレクトリを作りメディアルートとするのが一般的です。作成後はプロジェクトの.gitignore
**を修正してバージョン管理から除外します。
本番環境ではAmazon S3のバゲット等を設定します。
2.settings.pyの変更
設定ファイル(settings.py)に以下の変数を追加します。
-
MEDIA_ROOT
-
サーバから見たメディアルートの絶対パスです。開発環境では1で作ったディレクトリを指定します。
-
MEDIA_URL
-
メディアファイル公開時のURLのプレフィクスです。
メディアファイルのURLは「http://アプリのドメイン+MEDIA_URL+メディアファイル名」となります。
設定例:
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
###3.urls.pyの変更
ディスパッチャの設定(urls.py
)にメディアファイル公開用のURLを追加します。
urlpatterns = [
...
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
4.formタグの変更
テンプレート内の入力フォームのHTMLを修正します。フォームタグのenctype属性を設定します。
変更前:
<form method="post" id="myform">
変更後:
<form method="post" id="myform" enctype="multipart/form-data">
5.Viewのロジック変更 [任意]
関数ベースビューを使っている場合は、フォームに渡す引数を増やす必要があります。クラスベースビューでは修正不要です。
# 変更前
form = SampleForm(request.POST)
# 変更後
form = SampleForm(request.POST, request.FILES)
フィールド定義
基本定義
FileFieldの基本的な定義方法です。
attach = models.FileField(
upload_to='uploads/%Y/%m/%d/',
verbose_name='添付ファイル',
validators=[FileExtensionValidator(['pdf', ])],
)
-
upload_to
属性でMEDIA_ROOT
配下の保存先を指定します。strftime
フォーマットの指定が使える他、関数での指定が可能です。関数を使う場合は以下の資料が参考になります。 -
デフォルトのファイル名の制限は100文字までとなっています。もっと長いファイル名を使う場合は
max_length
属性で調整します。 -
ビルトインのバリデーション「FileExtensionValidator」で拡張子の制限をかけることができます。上記はpdfの例です。
ImageFieldについて
ImageFieldは画像を扱うことに特化したFileFieldの派生フィールドです
ImageFieldの利用には追加パッケージとしてPillow
が要求されます。
pip install Pillow
ImageFieldとFileFieldの主な違いは以下の点です。
-
登録時のバリデーションで画像ファイルのチェックをする。画像ファイルでないもの、データの破損があるものは登録エラーとする(専用エラーメッセージあり)。
-
管理用のフィールドを事前に用意すると、登録時に画像の高さと幅(pixel単位)を取得して保存する。
定義例:
- 登録時にurl_height と url_width に高さと幅が保存される
image = models.ImageField(
upload_to='files/',
verbose_name='添付画像',
height_field='url_height',
width_field='url_width',
)
url_height = models.IntegerField(
editable=False,
)
url_width = models.IntegerField(
editable=False,
)
Django ImageKit について
スマホで撮った高画質な画像をそのままアプリで扱うと、ファイルサイズが大きすぎて色々支障が出ることが危惧されます。追加パッケージのDjango ImageKit
を使うと、アップロード時にリサイズ処理やサムネイル作成処理をしてくれます。その他便利な機能が盛り込まれているので、画像ファイルを扱う際はDjango ImageKit
の利用を前提としましょう。
参考:Django ImageKit 公式
参考:DjangoでImageFieldからサムネイルをImageKitで自動生成する
問題の修正方法
保存時の問題
FileFieldのデフォルトの動作では、ファイルの削除・更新時に古いファイルは消されずそのまま残ります(同名ファイルで更新した場合、古いファイルがリネームされて残る)。これはこれでバックアップが残って安全な動作ですが、古いファイルが不要な場合は余計に容量が使われてしまいます。
この問題に対して「django-cleanup」というパッケージが提供されています。インストールして設定に入れるだけで、レコードの削除・更新時にファイルも削除されるようになります。
pip install django-cleanup
INSTALLED_APPS = [
...
'django_cleanup',
...
]
- 参考:django-cleanup公式
- 参考:Sugar Maple's blog 【Django】メディアファイルを更新・削除したときに、古いファイルを自動で削除してくれる「django-cleanup」が便利
表示面での問題
ファイル名だけを表示する
FileFieldにはMEDIA_ROOT
からの相対パスが保存されます。内容にディレクトリも含むので、そのまま画面表示で使うとユーザーが混乱します。ファイル名だけを表示させるには、テンプレートタグを使うのがスマートです。
import os
from django import template
register = template.Library()
@register.filter
def get_filename(value):
return os.path.basename(value.file.name)
{% load filename %}
{{item.file | getfilename }}
参考:Django: How to display only a filename in your template
エラーメッセージを正しく表示する
※django-crispy-forms利用時のみ
django-crispy-forms
を使っている場合、入力フォームのファイル入力欄のエラーメッセージが表示されません。django-crispy-forms
がファイル入力欄に対応していないのが原因のようです。
このままでは不便なのでcss
に以下の記述を追加してエラーメッセージを表示させます。
input[type="file"] + span.invalid-feedback {
display: block;
}
※[Djagno] ファイルアップロード機能の使い方 クラウドストレージ編 に続きます。