Djangoを使って画像アップロード、閲覧を実装してみる

こんにちは！やっしーです

今回は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に画像に対するパスを追加

```python:settings.py
IMAGE_ROOT = os.path.join(BASE_DIR, 'images')
IMAGE_URL = '/images/'
```

```python:PROJECT/urls.py
if settings.DEBUG:
    urlpatterns += static(settings.IMAGE_URL, document_root=settings.IMAGE_ROOT)
```

#### **これでユーザーが画像を参照できるようになる**

### 3. アップロードフォームの作成(+カラムの作成)
model定義

```python:models.py
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()

```

三行目の

```python
image = models.ImageField(upload_to='images',blank=True, null=True)
```
が画像のパスを保存するカラムっぽいupload_toはアップロード先を示す。

フォームの作成

```python:forms.py一部
class BookForm(forms.ModelForm):

    class Meta:
        model = Book
        fields = ('title', 'link','image')
```
ModelFormを使用していますが、必ずこうでなくてはいけない項目ではありません。

```html
<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リクエストでない時は投稿用画面をリターンする。登録時は登録処理が走る

```python:views.py一部
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})
```

ミソは


```python
book.image = request.FILES['image']
```
ここ。画像はファイルデータなのでこうしないと取れないらしい

これで登録はできます。ただ、templateの**enctype="multipart/form-data"**をつけないと画像が上手く保存されない（エラーもでない）ので要注意。ただし、adminではできます。なんででしょうね。

表示はこうする

```html
{% 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)までお願いします！