こんにちは!やっしーです
今回はDjangoを使って画像アップロード機能を実装しようとした時のつまづいた点含め手順を載せていこうと思います。
Goal 画像アップロードの実装
本のレビューサイトを作成するにあたり、本のタイトル、内容等に加え、その本の表紙が写った画像があるとわかりやすいと感じ、実装項目に追加した。
データベースのカラムに画像パスを保存。変数の設定を通じて保存先フォルダから引っ張ってこれる仕組みがDjangoにあります。ImageFieldというカラムです
環境
OS:Mac OS Mojave 10.14.6
- python 3.7
- Django 2.2
- Pillow 6.1.0
手順
1. 画像保存用のフォルダを作成する
django-admin startproject プロジェクト名にて作成されたプロジェクトフォルダ(以下PROJECT)直下(各種appと同階層)に、Imagesというフォルダ(名前は任意)を作成する。
2. PROJECTのsettings.pyで、画像保存パスを指定、urls.pyに画像に対するパスを追加
IMAGE_ROOT = os.path.join(BASE_DIR, 'images')
IMAGE_URL = '/images/'
if settings.DEBUG:
urlpatterns += static(settings.IMAGE_URL, document_root=settings.IMAGE_ROOT)
これでユーザーが画像を参照できるようになる
3. アップロードフォームの作成(+カラムの作成)
model定義
class Book(models.Model):
author = models.CharField(max_length=20)
image = models.ImageField(upload_to='images',blank=True, null=True)
title = models.CharField(max_length=32)
link = models.CharField(max_length=200)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
三行目の
image = models.ImageField(upload_to='images',blank=True, null=True)
が画像のパスを保存するカラムっぽいupload_toはアップロード先を示す。
フォームの作成
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = ('title', 'link','image')
ModelFormを使用していますが、必ずこうでなくてはいけない項目ではありません。
<form method="POST" class="post-form" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
アップロード処理のviews.py
Postリクエストでない時は投稿用画面をリターンする。登録時は登録処理が走る
def newBook(request):
if request.method == "POST":
form = BookForm(request.POST)
if form.is_valid():
book = Book()
print(request)
book.title = request.POST['title']
book.link = request.POST['link']
book.image = request.FILES['image']
book.author = request.user
book.published_date = timezone.now()
book.save()
return redirect('book_detail', pk=book.pk)
else:
form = BookForm()
return render(request, 'blog/new.html', {'form': form})
ミソは
book.image = request.FILES['image']
ここ。画像はファイルデータなのでこうしないと取れないらしい
これで登録はできます。ただ、templateの**enctype="multipart/form-data"**をつけないと画像が上手く保存されない(エラーもでない)ので要注意。ただし、adminではできます。なんででしょうね。
表示はこうする
{% for book in books %}
<div class="post">
{% if book.image %}
<div class ="image">
<img src="/{{ book.image.url }}" style="width:300px;height:auto">
</div>
{% endif %}
<div class="date">
{{ book.published_date }}
</div>
<h1><a href="{% url 'book_detail' pk=book.pk %}">{{ book.title }}</a></h1>
<p>{{ book.link|linebreaksbr }}</p>
</div>
{% endfor %}
コードはgithubに上がっています。わからないことあったら、お気軽にTwitter(id:Yasshieeee)までお願いします!