【Django】ImageFieldの使い方

ImageFieldの使い方のポイント

画像をアップロードする際に、DjangoのmodelでImage Fieldを使う場合があります。

他のFieldと違い設定をする必要があるので、初めて使う人のためにもメモとして残しておきます。

うまくいかないパターンで多いのは、下記のようなことだと思います。

  • うまくアップロードされない
  • ファイルを選択してPOSTで送ってもなぜか空orNoneになる。

具体的に設定のポイントを押さえておきましょう。

ファイルアップロードのポイント

設定のポイントは、以下の2つです。

  • アップロードファイルは、MEDIAフォルダに入る
  • HTMLからViewでPOSTで送る際、FILE形式で送る必要がある

MEDIAフォルダの設定

まずは、mediaを設定します。

一般的な配置の方法は、プロジェクト配下にmediaフォルダ新規作成します。。

sampleとしてdjango_projectというプロジェクトに作成すると...

django_project/
├config/
│    ├ __init__.py
│    ├ settings.py
│    ├ urls.py
│    └ wsgi.py
├django-app/
├media/     # 新規作成
├static/
├templates/
├ db.sqlite3
└ manage.py

次にsettings.pyの設定を行います。

settings.pyにmediaフォルダの設定を追記します。(一番下に追記してOK)

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, "media")

以上でmediaフォルダの設定が完了です。

ModelとFormの設定

Modelは、ImageFieldを宣言するだけです。

例えば、画像(image)とそれの説明(description)を宣言する場合は、下記のようにします。

class Images(models.Model):    
    image = models.ImageField(upload_to='img/upload')
    description = models.CharField(null=True, blank=True, max_length=256)

upload_toオプションで指定した文字列は、アップロード先のディレクトリを指します。

上記の例では、media/img/upload/配下に画像が保存されるように設定されています。

Formは、下記のように書けばOKです。

class ImagesForm(forms.ModelForm):
    class Meta:
        model = Images
        fields = ('image', 'description',)

TemplateとViewの設定

templateは、以下のように設定します。

 <form method="post" enctype="multipart/form-data">
  {% csrf_token %}
      <table>
          <tr><td>
              {{ form.images }}
             <!-- error表示-->
              {% if form.errors.image %}
                  <div class="alert alert-danger" role="alert">{{ form.image.errors }}</div>
              {% endif %}
          </td></tr>
          <tr><td>
              {{ form.description }}
              <!-- error表示-->
              {% if form.errors.description %}
                  <div class="alert alert-danger" role="alert">{{ form.description.errors }}</div>
              {% endif %}
          </td></tr>
          <tr><td>
              <button type="submit">Upload</button>
          </td></tr>
      </table>
  </form>

ポイントは、fromタグに、「enctype="multipart/form-data"」を追記することです。 これで、POSTリクエストを送る際に、フィル形式でサーバーに送ることができます。

続いて、View側の設定です。

今回は、CreateViewを使ったやり方を紹介します。

class ImagesCreateView(CreateView):
    template_name = 'img/image_form.html' # 上記のTemplateのパス
    form_class = ImagesForm # 上記のFormを設定

    def post(self, request, *args, **kwargs):   # リクエストがpostの際の処理をオーバーライドして記載
        form = ImagesForm(request.POST, request.FILES) 
        if not form.is_valid(): # validationでエラーがあれば、formに戻る(エラーメッセージを返す)
            return render(request, self.template_name, {'form': form}, )
        form.save() # formを保存
        return redirect(reverse('image:index')) # 成功した際の遷移先

上記のポイントは、postを受け取った際に、FILESを受け取ることです。

この記述があることで、ファイルを受け取ります。

form = ImagesForm(request.POST, request.FILES) 

以上で、formからファイルをアップロードできる設定は完了です。

試しに、動かしてみましょう。

※もし、不具合等あれば、コメント欄にてお願いします。

おまけ1:画像を表示する

uploadした画像を表示する場合は、

templateに以下のように記述することで、表示できます

(modelのパラメータをobjectとした場合)

<img src="{{ object.image.url }}"/>

おまけ2:保存する際のファイル名を変更する

例えば、formにfile_nameを入力して、入力されたファイル名を保存する場合は、以下のように、modelを記述します。

def get_upload_path(instance, filename):
    return 'img/'+filename

class Images(models.Model):
    file_name = models.CharField(max_length=30)
    image = models.ImageField(upload_to=get_upload_path)