djangoでモデルのフィールドに登録された画像のファイルを、viewsのdelete()を使用してもインスタンスに保存されているファイルは削除できません。
そうは問屋が卸さないのです。
そしてそのままスルーしたらどんどんゴミデータとして積もり積もってしまいます。
そんな時のファイルの削除の解決方法を紹介します。
・モデルのフィールドにある画像を削除しようとしているのに削除できない
・OS : Mac
・django 2.2
・python 3.8
それでは順番に説明をしていきます。
コンテンツ
削除できない事象に遭遇する例とクラスモデル
そもそもの今回のような事象に遭遇する例をモデルクラスと共に説明します。
デフォルトUserに、アバター画像と誕生日の追加をしたく新しくUserProfileというモデルクラスを作成しました。
ちなみにuser_idでリレーションを張っています。
models.py
from django.db import models
class UserProfile(models.Model):
user_id = models.IntegerField(
verbose_name = 'user_id',
null = False,
)
avator = models.ImageField(
verbose_name = 'avator',
upload_to = 'images/',
default = 'images/default_icon.png'
)
birthday = models.DateField(
verbose_name = 'birthday',
null=True,
blank=False
)
def __int__(self):
return self.user_id
viewsで削除できない
実際にviewsで書いたコードはこちらです(UpLoadProfileImgFormは割愛します)。
手順としては新しくプロフィールのアバター画像を変更したいので、13行目で既にプロフィールアバターがあるなら、14行目でそれを削除しますよ。という手順です。
views.py
from .models import UserProfile
from .forms import UpLoadProfileImgForm
def edit_profile_avator(request):
user_profile = UserProfile.objects.get(user_id=request.user.id)
if request.method != 'POST':
form = UpLoadProfileImgForm()
else:
form = UpLoadProfileImgForm(request.POST, request.FILES)
if form.is_valid():
avator = form.cleaned_data['avator']
if user_profile.avator:
user_profile.delete()
user_profile.user_id = request.user.id
user_profile.avator = avator
user_profile.save()
context = {
'form': form
}
return render(request, 'アプリ名/edit_avator.html', context)
新たにプロフィール画像を18行目で保存するのに、前のuser_idと同じ番号を持つavatorが存在するのは宜しくないですよね。
またこれ以外の理由としても画像をきちんと削除しておかないとゴミデータとしてどんどん積もり積もってしまうからです。
解決方法
解決方法としてはFileFieldに登録されたファイルを削除する事になりますが、方法を2つ紹介します。
views側からパス指定で削除
views側でdelete()するときにパス指定で削除実行のコードを追加します。
import os
os.remove('media/' + str(user_profile.avator))
こちらになりますが、先ほどのviewsに入れ込みます。
単純にremoveメソッドにパスを与えてあげれば良いのです。
ちなみに
settings.py
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
ここのパスは人それぞれかと思いますので調べて設定してください。
それでは先ほどのコードに追記した全体像がこちらになります。
15行目に追記しました。
views.py
import os
from .models import UserProfile
from .forms import UpLoadProfileImgForm
def edit_profile_avator(request):
user_profile = UserProfile.objects.get(user_id=request.user.id)
if request.method != 'POST':
form = UpLoadProfileImgForm()
else:
form = UpLoadProfileImgForm(request.POST, request.FILES)
if form.is_valid():
avator = form.cleaned_data['avator']
if user_profile.avator:
os.remove('media/' + str(user_profile.avator))
user_profile.delete()
user_profile.user_id = request.user.id
user_profile.avator = avator
user_profile.save()
context = {
'form': form
}
return render(request, 'アプリ名/edit_avator.html', context)
modelsにデコレーターを設定
models.py
from django.db.models.signals import post_delete
from django.dispatch import receiver
# モデル削除後にavatorを削除する
@receiver(post_delete, sender=UserProfile)
def delete_file(sender, instance, **kwargs):
instance.avator.delete(False)
1行目の from django.db.models.signals import post_delete の post_delete ですがこちらはviews側のdelete()メソッドの処理の最後に呼び出されます。
5行目のsenderにはモデルクラスを指定します。
全体像としてはこうなります。
models.py
from django.db import models
class UserProfile(models.Model):
user_id = models.IntegerField(
verbose_name = 'user_id',
null = False,
)
avator = models.ImageField(
verbose_name = 'avator',
upload_to = 'images/',
default = 'images/default_icon.png'
)
birthday = models.DateField(
verbose_name = 'birthday',
null=True,
blank=False
)
def __int__(self):
return self.user_id
@receiver(post_delete, sender=UserProfile)
def delete_file(sender, instance, **kwargs):
instance.avator.delete(False)
※参考 https://docs.djangoproject.com/ja/2.1/topics/signals/
プログラミング学習を効率良く進めるには…
私ヒロヤンがプログラミングを始めた頃は以下のような感じでした。
何から手をつけていいのか、わからない。
プログラミングを始めてみたけど、エラーが解決できない。
そしてネットで調べていくうちに膨大な時間が過ぎていきました。
私ヒロヤンの実体験より、プログラミングを効率的に学ぶために大切なことは以下のことだと考えています。
1. いつまでもダラダラとやらないで、目標を決定して短期集中する
2. マンツーマンで、わからない箇所は直ぐに質問をして即レスをもらう
.proでは私ヒロヤンが学習してきたプログラミング経験0からのpython/django、その他webサイト・サービス開発のコースが用意されています。
カウンセリング自体は無料なので話を聞いてみるだけでもいかがでしょうか?
また以下のリンク先ではdjangoを教えてくれるスクールをまとめ紹介しています。




コメントを残す