こんにちは。sinyです。
この記事では、Djangoでファイル一覧情報を更新する方法について紹介します。
想定ケース
メディアファイル設定を使って画面からファイルをサーバにアップロードした後、アップロードされたファイルをリスト形式で一覧表示するといった場合(下図)、普通にフォームの定義をしただけでは設定しただけでは情報が更新されません。
更新するには開発サーバの再立ち上げをしないとダメ…なんて状況に陥ることがよくありました。
これでは実用的ではないため、画面を再表示するタイミングで自動的にファイルリストが最新化されるようにします。
モデルを利用した場合の手順
まず、モデルは以下のようにFileFieldを使ったフィールドを1つ用意しておきます。
from django.db import models class UploadFile(models.Model): class Meta: verbose_name = 'データのアップロード' verbose_name_plural = 'データのアップロード' file = models.FileField(verbose_name="file Upload", upload_to='csv/') def __str__(self): """ファイルのURLを返す""" return self.file.url
メディアの設定は以下とします。
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = '/media/'
続いてforms.pyの定義です。
from django import forms from .models import UploadFile from django.conf import settings class UploadFileForm(forms.Form): fname = forms.ChoiceField(label='データファイルの選択', choices=lambda:[(os.path.join(os.path.join(settings.MEDIA_ROOT, "csv"), fname.file.name.split("/")[1]), \ fname.file.name.split("/")[1]) for fname in UploadFile.objects.all()], required=False))
ポイントは以下の部分です。
choices=lambda:[(os.path.join(os.path.join(settings.MEDIA_ROOT, "csv"), fname.file.name.split("/")[1]), \ fname.file.name.split("/")[1]) for fname in UploadFile.objects.all()], required=False))
ChoiceFieldフィールドオプションのchoicesでlamba関数を使いUploadFileテーブルクラスから全レコードを取得し、ファイルパス情報を取得して(ファイルの絶対パス、ファイル名)の形式でタプルのデータを生成します。
UploadFileテーブルのfile列にはcsv/<ファイル名.csv>のような情報が格納されているので、fname.file.name.split("/")[1]とすることで<ファイル名.csv>の部分だけを抽出しています。
os.path.join(os.path.join(settings.MEDIA_ROOT, "csv"))はcsvが格納されている絶対パスを取得しています。
以上で、画面からファイルアップロードしたあと画面を更新するとファイル一覧が最新状態に更新されるようになります。
モデルを利用しない場合の手順
先ほどの例ではmodels.pyでFileFieldを使っていたので、ファイルアップロードをするとテーブルにファイルの情報が登録されるため、テーブルレコードの情報を利用することでファイル一覧情報を最新に更新しました。
では、モデルに定義がないケースではどうするか?
たとえば、モデルとしては情報を持っていないが、サーバ上に配置されているファイルの一覧情報をリスト形式で一覧表示したいケースです。
この場合、forms.pyでフォームの定義をしますが、モデルの定義がないため先ほどの方法は使えません。
例えば以下のようにmedia/dataフォルダ配下に存在するファイル一覧をリスト形式で一覧表示するという事例で説明します。
from django.core.files.storage import default_storage from django.conf import settings import os def file_list(): data_path = os.path.join(settings.MEDIA_ROOT, "data") _, file_list = default_storage.listdir(os.path.join(settings.MEDIA_ROOT, "data")) FILE_LIST = [(os.path.join(data_path, file_name), file_name) for file_name in file_list] return FILE_LIST class FileForm(forms.Form): fname = forms.ChoiceField(label='ファイルの選択', choices=file_list())
上記の通り、ChoiceFieldを使ってchoicesフィールドに該当のディレクトリに存在しているファイル一覧の情報を渡してあげればリスト形式で画面表示されるフォームが出来上がります。
file_list()はmedia/data配下のファイル一覧を取得して(ファイルの絶対パス,ファイル名)というタプルを生成する関数です。
しかし、この状態だとmedia/data配下にファイルが追加、削除された後に画面を更新してもリスト情報が更新されません。
更新するには開発サーバを再立ち上げなどしないと更新されず、これでは実用的ではありません。
画面更新時にリスト情報を最新状態に更新するには、以下の通り__init__メソッド内でchoicesフィールドを更新する処理を書くだけで実現できます。
from django.core.files.storage import default_storage from django.conf import settings import os def file_list(): data_path = os.path.join(settings.MEDIA_ROOT, "data") _, file_list = default_storage.listdir(os.path.join(settings.MEDIA_ROOT, "data")) FILE_LIST = [(os.path.join(data_path, file_name), file_name) for file_name in file_list] return FILE_LIST class FileForm(forms.Form): fname = forms.ChoiceField(label='ファイルの選択', choices=file_list()) #ここから追加 def __init__(self, param): forms.Form.__init__(self, param) self.fields['model_fname'].choices = get_model_list()
以上、Djangoでファイル一覧情報を更新する方法でした。