4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DjangoとJavaScriptで画像複数同時アップロードの実装

Last updated at Posted at 2020-07-16

本記事について

Djangoのライブラリでは,画像を複数同時にアップロードし,データベースに記録するという機能がない.ネットで調べても方法が見あたらなかったため,JavaScriptと組み合わせることで実装した.そのノウハウを備忘録及び,同じ問題に直面した人に貢献できるようにと記事を投稿する.

#もくじ

  • 実装方法の説明
  • Djangoの初期設定
  • views.pyの記述
  • テンプレートの記述
  • 注意点
  • まとめ
  • 参考文献

##実装方法の説明
まず前提知識として,Djagoのモデルは,ImageFieldにおいて1フィールドにつき1つの画像しか保存することができない.したがって複数の画像を同時アップロードしたい場合は,必要な数だけフィールドを作成する必要がある.
以下に具体的な実装方法の手順を示す.なお,各詳細は後に説明する.

  1. HTMLのmultipleのinputタグを作成
  2. モデルフォームでアップロード枚数の上限とフォームセットを生成
  3. 送信と同時に,JavaScriptのDataTransferで選択された画像の数だけオブジェクトを生成し,別々のinputタグに格納する
  4. サーバーサイドのviews.pyで,formsetをデータベースに保存する

Djangoの初期設定

ここでは,実際に実装する前に,一例として開発するtest用のDjangoの各ファイルの初期設定を行う.
以下に,urls.py, models.py, forms.pyについてのコードを記す.
なお,urls.pyのuploadfuncは後にviews.pyに記述する関数である.

urls.py
from django.urls import path, include
from .views import uploadfunc

urlpatterns = [
    path('upload/', uploadfunc, name='upload'),
]

models.py
from django.db import models

class TestModel(models.Model):
    files = models.ImageField('画像ファイル', null=True, blank=True, default='')
forms.py
from django import forms
from .models import TestModel

class SingleUploadModelForm(forms.ModelForm):
    class Meta:
        model = TestModel
        fields = '__all__'

views.pyの記述

EXTRAは,アップロードする画像の上限枚数を意味している.
formsetはDjangoのformsのライブラリ(フォームモデル)を使用する.

views.py
from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from .models import TestModel
from django import forms
from .forms import SingleUploadModelForm

def uploadfunc(request):
    EXTRA = 5
    UploadModelFormSet = forms.modelformset_factory(TestModel, form=SingleUploadModelForm,extra=EXTRA)
    formset = UploadModelFormSet(request.POST or None, files=request.FILES or None, queryset=TestModel.objects.none())
    if request.method == 'POST': #画像が選択され,送信されたとき
        formset.save() #データベースに保存
    context = {
        'form': formset,
        'number_list': list(range(EXTRA)),
        'total_number': EXTRA,
    }
    return render(request, 'upload.html', context)

テンプレートの記述

views.pyのcontextで,'form'を返している.これは,テンプレートで{{ form }}と,フォームオブジェクトを変数表示させるだけで,入力データがセットされた入力フィールドを全て出力することができる.セットされたinputタグには,それぞれデフォルトでidがつけられており,画像ファイルでは,id_form-num-filesというidがつけられている.なお,今回はEXTRAは5と設定したため,5つのinputタグがセットされ,idはnumの部分に0~4までの数字が入る.
今回,複数画像を選択するため,{{ form }}とは別に,multipleを有効としたinputタグを準備する.JavaScriptのDataTransfer()機能を使うことで,選択された画像を{{ form }}のinputタグ内にそれぞれ格納する.そうすることで,送信を完了させると,選択された画像がviews.pyへ返され,複数同時選択とアップロードが実現する.

以上のことを以下の画像で説明する.これは,EXTRA=5,そして画像を3枚選択したときの例である.
説明図2.PNG

今回{{ form }}の部分は非表示にしておく.送信ボタンが押されたとき,JavaScriptの関数(multipleFunction)を呼び出し,完了後views.pyへ返す.
以下にテンプレートファイルのコードと結果を示す.

upload.html
<form action="" method="POST" enctype="multipart/form-data" style="margin: 10px;">
    <div hidden>{{ form }}</div>
    {% csrf_token %}
    <p><同時に複数の写真を選択できます></p>
    <input type="file" multiple accept="image/*" id="multiple-files">
    <br>
    <button id="sending" type="submit" class="sending" onclick="multipleFunction()">送信</button>
</form>

<script type='text/javascript'>
    function multipleFunction() {
        for (let i = 0; i < document.getElementById('multiple-files').files.length; i++) {
            const dt = new DataTransfer();
            dt.items.add(document.getElementById('multiple-files').files[i]);
            document.getElementById("id_form-%number-files".replace("%number", i)).files = dt.files;
        }
    }
</script>

結果
結果1.PNG

注意点

以上により画像複数同時アップロード機能の実装が完了した.
しかし,ここで注意点が2つがある.
1つ目は,JavaScriptのDataTrandsferを対応していないブラウザが多数あるという点である.当サイトによると,特にiOSのブラウザでは現在非対応なため,iPhoneやmacでは使用できないず,要注意である.

2つ目は,今回画像のみのアップロードをしたが,その他にメモや日付などといった他のデータを同時に保存したい場合は,上記のやり方では対応できないという点である.
例えばEXTRA=5に対して3枚の画像のみをアップロードする場合,差の2つは保存されないためうまくいく.しかし,他のフィールドを同時に保存しようとした場合,5つ全て保存してしまうため,画像が2つ選択されていないというエラーが生じてしまう.
今回その突破口については別の機会に記述するが,ヒントとしては,上記の例で言うと5つアップロードした後に,余計なを2つ削除するという記述をviews.pyに書く,ということである.

まとめ

画像同時アップロードの実装方法を記述した.今回は,JavaScriptのDataTransfer()機能を用いて実装した.少々説明がわくりにくかったかもしれないが,わからなかった場合は各ファイルのコードを参考にして頂きたい.

参考文献

  1. 横瀬明仁, 現場で使えるDjangoの教科書<<基礎編>>,2019
4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?