Djangoでストレージを節約しながらファイル・画像を扱う|画像プレビュー

Djangoでストレージを節約しながらファイル・画像を扱う|画像プレビュー プログラミング
Djangoでストレージを節約しながらファイル・画像を扱う|画像プレビュー
スポンサーリンク

こんにちは、こがたです。

Djangoでストレージを節約しながらファイル・画像を扱う方法を紹介します。

フォームからファイル・画像フィールドを削除、編集したときに、使用しなくなった画像を削除しなければ、不要なファイルがたまっていき、ストレージが圧迫されます。

この記事では画像プレビューがあるフォームで画像を投稿しつつ、自動的に不要な画像を削除するコードを説明します。

対象読者
  • Django利用者
  • ファイル、画像フィールドを利用している
  • ストレージを気にしてなかった
  • 画像プレビューを実装したい

最初にフォーム画面で画像プレビューを利用しながら登録する方法を紹介します。
画像フィールドの扱い方も理解できます。

次に不要なファイルを自動的に削除する方法を紹介します。

ファイルフィールドは画像フィールドと同様に扱えます!

フォーム画面で画像プレビューを表示して登録

最終的に↓↓↓の状態になるよう実装していきます。

画像登録

ImageFieldsにて画像ファイルを選択したら、プレビューが表示され、登録後に画像が表示される詳細画面に遷移します。

こがた
こがた

画像のプレビュー、登録についてご存知の方は読み飛ばしてください!

画像を読み込むため(settings.py,urls.py)

まず画像を扱うために「pillow」というモジュールをインストールする必要があります。

以下のコマンドを実行してインストールしておいてください。
※Djangoで画像を利用する場合必須

pip install pillow

次に画像を保存するディレクトリを「settings.py」に設定します。
以下を追加してください。

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

プロジェクトディレクトリ配下に「media」というディレクトリを作成してください。
「media」というディレクトリ名は自由に変更可能です。

このディレクトリの下に登録した実ファイル・画像が保存されていきます。

URLから画像を読み込めるように「プロジェクトのurls.py」に以下を追加してください。

from django.conf import settings      
from django.conf.urls.static import static

# 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜

urlpatterns += static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)

これで「http://○○○/media/サブディクトリ/画像ファイル」というURLで画像を参照できるようになります。

画像を扱うモデル、ビューの設定

この記事では「images」というアプリを作成しています。

ここからは画像を登録するmodels,viewsを説明します。
画像が保存されるサブディレクトリ設定に関係します。

from django.db import models

class ImageContent(models.Model):
    img = models.ImageField(
        upload_to='img/',
        blank=True,
    )

「ImageField」の引数”upload_to”にて画像が保存されるサブディクレトリを指定することができます。

この記事の場合「プロジェクト名/media/img/」ディレクトリ配下に画像が保存されていきます。

Fieldごとに保存するサブディクトリを変更できるので管理しやすいよう、設定してください。

views.py」は以下のようになります。

from django.shortcuts import render
from django.urls import reverse
from django.views import generic

from .models import ImageContent
from .forms import ImageContentForm

class ImageDetail(generic.DetailView):
    model = ImageContent
    template_name = 'images/detail.html'

class ImageCreate(generic.CreateView):
    model = ImageContent
    form_class = ImageContentForm
    template_name = "images/image_add.html"

    def get_success_url(self):
        return reverse('images:detail', kwargs={'pk' : self.object.pk})
 
    def form_valid(self, form):
        result = super().form_valid(form)
        return result

ここでは汎用ビューを使用していますが、独自で実装する場合、formを呼び出す際に「request.FILES」を引数に入れる必要があります。

form = ImageContentForm(request.POST or None, request.FILES)
こがた
こがた

ぼくはいつも、この設定を忘れて、デバッグに時間がかかっているので要注意です〜!!!

views.pyで呼び出している「form.py」はこちらです。
ここでは特に変わったことはしてないので説明は省略します

from django import forms

from .models import ImageContent

class ImageContentForm(forms.ModelForm):
    class Meta:
        model = ImageContent
        fields = ('img',)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'

画像プレビューを表示するテンプレート

ここでは「base.html」という共通パーツを読む形にしています。
templatesに関しての説明はこの記事では省略します。

<!doctype html>
<html lang="ja">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"
          integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
    {% block extrajs %}{% endblock %}

    <title>画像登録サンプル</title>
</head>
<body>
    <!-- メインコンテント -->
    <div class="container mt-3">
        {% block content %}{% endblock %}
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
            integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
            crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js"
            integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ"
            crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"
            integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm"
            crossorigin="anonymous"></script>
</body>
</html>

「{% block extrajs %}{% endblock %}」に、各ページごとのJSコードを記載するようにしています。

画像プレビューには「JQuery」を使用します。

登録フォームのテンプレートはこちらです。

{% extends "images/base.html" %}
{% block content %}

<form method="post" enctype="multipart/form-data" novalidate>
    {% csrf_token %}

    {{ form }}

    <button class="btn btn-primary center-block btn-lg" type="submit">保存する</button>

    <br>

    <img id="image-preview"> 
</form>
{% endblock %}

{% block extrajs %}
    <script>
        // 画像の変更
        $(document).on('click', '#image-preview', function() {
            $('#id_img').click();
        });

        // 画像のプレビュー
        $(document).on('change', ':file', function() {
            var input = $(this),
            numFiles = input.get(0).files ? input.get(0).files.length : 1,
            label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
            input.parent().parent().next(':text').val(label);

            var files = !!this.files ? this.files : [];
            if (!files.length || !window.FileReader) return; 
            if (/^image/.test( files[0].type)){
                var reader = new FileReader();
                reader.readAsDataURL(files[0]);
                reader.onloadend = function(){
                    $("#image-preview").attr('src', this.result);
                }
            }
        });
    </script>
{% endblock %}

<form>タグの属性に「enctype=”multipart/form-data”」を入れないとファイル・画像を扱うことができないので注意してください。

HTMLにて画像要素を作成しておき、画像をクリックすることでファイル選択画面が表示されるようにしています。
ファイルフィールドに「display:none」など設定しておけば、画像をクリックしてアップロード画像を指定するシンプルなフォームにすることができます!

画像が選択されると、imgタグのsrc属性をフロント側で変更して表示するようにしています。

こがた
こがた

imgタグのidとJSのID指定の箇所を変更したらどこでも利用できます!

Djangoで自動的に不要なファイル・画像を削除する

Djangoのデフォルト設定では画像を削除、変更しても保存された物理ファイルは削除されません!

そのため、不要なファイルがたまっていきストレージを圧迫してしまいます。

【test1.png という画像を登録】
→/media/img
      |-test1.png

【test1.png を test2.png に変更】
→/media/img
      |-test1.png
      |-test2.png

django-cleanup」というモジュールを利用すれば、自動的にファイルを削除してくれます。

以下のコマンドを実行してインストールしてください。

pip install django-cleanup

つぎにプロジェクトの「settings.py」の”INSTALLED_APPS“にこのように追加してください。

INSTALLED_APPS = (
    〜〜〜〜〜〜,
    'django_cleanup.apps.CleanupConfig',
)
こがた
こがた

これだけで自動的に不要なファイルを物理削除してくれます!!!

【test1.png という画像を登録】
→/media/img
      |-test1.png

【test1.png を test2.png に変更】
→/media/img
      |-test2.png

【+α】同じファイル名をアップロードしたとき

画像ファイルを扱っていたときにこのよな疑問が、、、

こがた
こがた

アップロードしたときのファイル名で保存されるのなら、同じファイル名で別の画像をアップロードしたら上書きされるのでは、、、?

Djangoはデフォルトで同じ名前のファイルをアップロードしたとき、ユニークなファイル名に変更してくれます!

ファイル名についてはなにも気にしなくて良い仕様になっています。

最後に

Djangoには数多くの外部モジュールがあり、自分で実装する手間をはぶいてくれます。

Webアプリを開発したい場合はPythonのWebフレームワーク『Django』をオススメします。

Python初心者はPyQで学習することで早く、的確にスキル習得ができます!
「プログラミングとは?」からWeb開発、AI開発の基本を教えてくれます。

中級者以上には物足りないかもしれません、、、

中級者以上は『Udemy』で学習する方法がオススメです!
全部で10万以上の講座があり、買い切り型です。

そのため、学びたい箇所をピックアップして集中的に学習できます。
買い切り型なので、いつでも・どこでも・何回でも受講可能です。

セール中は1万円代の講座を1,000円代で購入できるので、狙ってみてください。

最後まで読んでくださり、ありがとうございました!!!

コメント

タイトルとURLをコピーしました