69
67

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 (7) 画像データの管理やページへの表示,アップロードの方法などについて知ろう

Last updated at Posted at 2019-04-11

はじめに

このドキュメントは,もともとはラボの新メンバー向けの入門テキストとして作成したもので,はじめてDjangoにふれる人にざっとその全体像をつかんでもらうことを狙っています.Djangoの日本語での参考資料も充実してきたので今さらという気がしないでもないですが,見直しを機にQiitaに移すことにしました.万が一でもどなたかの参考になれば幸いです.

説明のための具体例として,Djangoのオフィシャルチュートリアルにある投票アプリをとりあげています(が,説明上の都合で少しコードを追加,変更しているところもあります).素人の独学がベースで,特に前半は公式チュートリアルをやってみた感想のような記事です(今回は全7回中の最終回).

全体のコードはまとめてGitHubに置きました.

変更履歴

  • [2021/04/26] Djangoのバーションを3.2に更新し,それ基づいて内容を微修正しました.

画像の表示とアップロード

今回は,画像ファイルをデータベースで管理し,ページに表示する方法や,ユーザに画像ファイルをアップロードしてもらう方法をみていきます.

なお,Djangoで画像ファイルを扱うためにはPillowという画像処理ライブラリが必要になるので,まだインストールしていない場合は,下記のコマンドであらかじめインストールしておきましょう.

$ pip install pillow  # pipenvを利用している場合は pipenv install pillow

最初に,mysiteのプロジェクト内に,pollsとは別に,albumというアプリケーションを作成します.

$ python manage.py startapp album

外側のmysiteのディレクトリで上記のコマンドを打ち込むと,その中にalbumというディレクトリが新たに作成されます(ここで忘れずに,settings.pyのINSTALLED_APPSのリストにこのアプリケーションを追加しておきましょう).

続いて,画像を扱う簡単なモデルを作成します.具体的には,albumディレクトリ内のmodels.py(album/models.py)の中に下記の内容を書き足します.

from django.db import models

class Image(models.Model):
    picture = models.ImageField(upload_to='images/')
    title = models.CharField(max_length=200)

    def __str__(self):
        return self.title

これは,画像ファイルを表すpictureとその表題を表すtitleという2つのfieldをもった,Imageクラスの定義になっています(なお,このmodels.ImageField()を使うためにPillowが必要になるわけです).

画像ファイルの実体はデータベース内に格納されるわけではなく,あるディレクトリにアップロードされることになります.models.ImageField()の引数に指定されているupload_toでこのディレクトリを指定しています.

上の例では,upload_toにimages/というディレクトリが指定されていますが,これはMEDIA_ROOTを起点とした相対パス指定になります.また,MEDIA_ROOT内の画像ファイルをurlで参照する際に起点となるアドレスをMEDIA_URLとして指定しておく必要があります.

今回は,外側のmysiteディレクトリ内にmediaというディレクトリを作成し,それをMEDIA_ROOTに指定しましょう.また,MEDIA_URLもわかりやすくmediaという名称にしておきます.具体的には,settings.pyの中に下記のように指定しておくことにします.

BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_ROOT = BASE_DIR.joinpath('media')
MEDIA_URL = '/media/'

BASE_DIRはデフォルトのままですが,具体的には外側のmysiteディレクトリを指すことになります.MEDIA_ROOTはその中のmediaディレクトリという意味にります.また,MEDIA_URLをこのように設定すると,開発用サーバを用いている場合,http://localhost:8000/media/ が画像ファイルのurlの起点となります.

次に,このモデルをadminサイトに登録して,adminサイトでImageクラスの画像ファイルを操作できるようにしましょう.まず,album/admin.pyに次の内容を書き込みます.

from django.contrib import admin
from .models import Image

admin.site.register(Image)

makemigrationsとmigrateでデータベースの構成を更新してから,開発用サーバを立ち上げ,adminサイトにアクセスしてみましょう.追加したImageクラスが現れているはずです.

これでadminサイトを通じて,画像ファイルをデータベースにアップロードしたり,データベースから削除したりできるようになりました.実際に画像ファイルをいくつかアップロードしてみましょう.

続いて,画像ファイルを表示するページを作成します.このためにまず,album/views.pyの中に次のような簡単なview関数を用意しましょう.

from django.shortcuts import render
from .models import Image

def showall(request):
    images = Image.objects.all()
    context = {'images':images}
    return render(request, 'album/showall.html', context)

データベース内にあるすべてのImageクラスのオブジェクトを取得して,それをimagesという名称でtemplateに渡していることがわかります.

次に,templateを用意します.具体的には,album/templates/album/showall.htmlというファイルを作成し,下記の内容を書き込みます.

<h1>Your Album</h1>

{% for image in images %}
    <img src='{{ MEDIA_URL }}{{image.picture}}' width=200>
    <h2>{{ image.title }}</h2>
{% endfor %}

albumアプリケーションに関するurlルーティングは,album/urls.pyというファイルを作成し,その中に指定するようにします.ここでは,ひとまず次のように指定しておけばよいでしょう.

from django.urls import path
from . import views

app_name = 'album'

urlpatterns = [
    path('showall/', views.showall, name='showall'),
]

また,あわせてmysite/urls.pyの方を次のように更新しておきます.

from django.contrib import admin
from django.urls import include, path
from django.views.generic import base
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('polls/', include('polls.urls')),
    path('album/', include('album.urls')),
    path('admin/', admin.site.urls),
    path('accounts/login/', base.RedirectView.as_view(pattern_name="polls:login")),
    path('accounts/profile/', base.RedirectView.as_view(pattern_name="polls:index")),
] +  static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

urlpatternsの2行目の項目で,album/以降のルーティングを,上で作成したalbum/urls.pyに引き継ぐように指定していることがわかります.

なお,最後に追加されているstatic()は,開発用サーバが,MEDIA_ROOT以下のディレクトリに格納されているファイルをMEDIA_URLを起点としたurlで供給できるようにするための設定です(詳しくはここを参照).

これでOKといいたいところですが,もう1つだけ準備が必要です.settings.pyのTEMPLATESの中のcontext_processorsに次の項目を追加しておきます(そうでないとtemplate内でMEDIA_URLの値が参照できないそうです).

TEMPLATES = [
    {
        ...
        'OPTIONS': {
            'context_processors': [
                ...
                'django.template.context_processors.media', # これを追加する
            ],
        },
    },
]

以上で準備は整ったはずです.http://localhost:8000/album/showall/ にアクセスして画像が表示されることを確認してみましょう.

最後にユーザが画像ファイルをアップロードするためのページを作成しておきましょう.まず,Imageクラスのデータ(画像ファイルとそのタイトル)を取得するためのフォームを作成します.album/forms.pyに次の内容を書き込みます.

from django import forms
from .models import Image

class ImageForm(forms.ModelForm):
    class Meta:
        model = Image
        fields = ['picture', 'title']

今回はModelFormという仕組みを利用しました.これは,あるモデルに対応した形式でデータを取得したい場合に,モデルの定義を利用してフォームを簡単に定義する仕組みです(詳細はここを参照).class Metaの中をみると,これはImageクラスに対応しており,pictureとtitleのデータを取得するためのフォームであることがわかります.

続いて,album/views.pyの中にアップロード用のview関数upload()を用意します.

from django.shortcuts import render, redirect
from .models import Image
from .forms import ImageForm

...

def upload(request):
    if request.method == "POST":
        form = ImageForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return redirect('album:showall')
    else:
        form = ImageForm()

    context = {'form':form}
    return render(request, 'album/upload.html', context)

これは,フォームを利用してユーザからデータを取得する場合の典型的なパターンになっていますが,2点だけ補足しておきます.

まず,POSTのリクエストを受けた際にImageForm()request.POSTだけではなく,request.FILESも渡している点です.これはPOSTで画像(に限らず他の種類であっても)ファイルを取得する際に必要になるので頭の片隅においておきましょう.

2点目は,form.save()です.上のImageFormクラスでは特にsave()メソッドは定義していませんが,form.ModelFormを継承しているので,デフォルトのsave()メソッドが機能します.

具体的には,モデルのインスタンスを渡した場合はそれが取得データで上書きされ,今回のように,インスタンスを渡さなかった場合は新しいインスタンスが生成,保存されることになります(詳しくはここを参照).

簡単なtemplateも用意しておきましょう.album/templates/album/upload.htmlのファイルを作成し,次の内容を書き込みます.

<h1>Please Upload</h1>

<form enctype="multipart/form-data" action="{% url 'album:upload' %}" method="post">
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="Upload">
</form>

ここでのポイントは,<form>タグの属性としてenctype="multipart/form-data"を指定しておくことです.この指定がないとファイルをDjangoに渡せません.

最後にurlルーティングの情報をalbum/urls.pyに追加します.具体的には,urlpatternsの中に下の記述を追加します.

path('upload/', views.upload, name='upload'),

ついでに,showallのページからuploadのページにリンクを張っておきましょう.album/templates/album/showall.htmlの末尾に次の1行を追加します.

<h2><a href="{% url 'album:upload' %}">Upload more</a></h2>

以上で準備が整ったので,開発サーバを立ち上げて,http://localhost:8000/album/upload/ にアクセスすると画像のアップロードができるはずです.

おわりに

以上で最終回まで終了です. m(__)m

69
67
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
69
67

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?