Saya sedang membangun aplikasi web di Django. Saya memiliki model yang mengupload file, tetapi saya tidak dapat menghapusnya. Ini kode saya:
class Song(models.Model):
name = models.CharField(blank=True, max_length=100)
author = models.ForeignKey(User, to_field='id', related_name="id_user2")
song = models.FileField(upload_to='/songs/')
image = models.ImageField(upload_to='/pictures/', blank=True)
date_upload = models.DateField(auto_now_add=True)
def delete(self, *args, **kwargs):
# You have to prepare what you need before delete the model
storage, path = self.song.storage, self.song.path
# Delete the model before the file
super(Song, self).delete(*args, **kwargs)
# Delete the file after the model
storage.delete(path)
Kemudian, di "python manage.py shell" saya melakukan ini:
song = Song.objects.get(pk=1)
song.delete()
Ini menghapus dari database tetapi bukan file di server. Apa lagi yang bisa saya coba?
Terima kasih!
django
django-models
Marcos Aguayo
sumber
sumber
Jawaban:
Sebelum Django 1.3, berkas telah dihapus dari sistem berkas secara otomatis ketika Anda menghapus contoh model terkait. Anda mungkin menggunakan versi Django yang lebih baru, jadi Anda harus menerapkan menghapus berkas dari sistem berkas sendiri.
Anda dapat melakukannya dengan beberapa cara, salah satunya dengan menggunakan sinyal
pre_delete
ataupost_delete
.Contoh
Metode pilihan saya saat ini adalah campuran
post_delete
danpre_save
sinyal, yang membuatnya sehingga file usang akan dihapus setiap kali model terkait dihapus atau file mereka diubah.Berdasarkan
MediaFile
model hipotetis :import os import uuid from django.db import models from django.dispatch import receiver from django.utils.translation import ugettext_lazy as _ class MediaFile(models.Model): file = models.FileField(_("file"), upload_to=lambda instance, filename: str(uuid.uuid4())) # These two auto-delete files from filesystem when they are unneeded: @receiver(models.signals.post_delete, sender=MediaFile) def auto_delete_file_on_delete(sender, instance, **kwargs): """ Deletes file from filesystem when corresponding `MediaFile` object is deleted. """ if instance.file: if os.path.isfile(instance.file.path): os.remove(instance.file.path) @receiver(models.signals.pre_save, sender=MediaFile) def auto_delete_file_on_change(sender, instance, **kwargs): """ Deletes old file from filesystem when corresponding `MediaFile` object is updated with new file. """ if not instance.pk: return False try: old_file = MediaFile.objects.get(pk=instance.pk).file except MediaFile.DoesNotExist: return False new_file = instance.file if not old_file == new_file: if os.path.isfile(old_file.path): os.remove(old_file.path)
save()
(misalnya dengan mengupdate massal aQuerySet
), file lama akan tetap ada karena sinyal tidak akan dijalankan. Ini tidak terjadi jika Anda menggunakan metode penanganan file konvensional.file
sebagai nama bidang, yang bukan merupakan gaya yang baik karena bentrok denganfile
pengenal objek bawaan.Lihat juga
FieldFile.delete()
dalam referensi bidang model Django 1.11 (perhatikan bahwa ini menjelaskanFieldFile
kelas, tetapi Anda akan memanggil.delete()
langsung pada bidang:FileField
proxy instance keFieldFile
instance terkait , dan Anda mengakses metodenya seolah-olah mereka adalah bidang)Mengapa Django tidak menghapus berkas secara otomatis: entri dalam catatan terbitan untuk Django 1.3
Contoh penggunaan
pre_delete
sinyal sajasumber
instance.song.delete(save=False)
, karena menggunakan mesin penyimpanan django yang benar.os.path.isfile(old_file.path)
gagal karenaold_file.path
menimbulkan kesalahan (tidak ada file yang terkait dengan bidang). Saya memperbaikinya dengan menambahkanif old_file:
sebelum panggilan keos.path.isfile()
.Coba django-cleanup , ini secara otomatis memanggil metode delete pada FileField ketika Anda menghapus model.
pip install django-cleanup
settings.py
INSTALLED_APPS = ( ... 'django_cleanup', # should go after your apps )
sumber
Anda dapat menghapus berkas dari sistem berkas dengan
.delete
metode pemanggilan bidang berkas yang ditunjukkan seperti di bawah ini dengan Django> = 1.10:obj = Song.objects.get(pk=1) obj.song.delete()
sumber
Anda juga dapat dengan mudah menimpa fungsi delete dari model untuk memeriksa file apakah ada dan menghapusnya sebelum memanggil fungsi super.
import os class Excel(models.Model): upload_file = models.FileField(upload_to='/excels/', blank =True) uploaded_on = models.DateTimeField(editable=False) def delete(self,*args,**kwargs): if os.path.isfile(self.upload_file.path): os.remove(self.upload_file.path) super(Excel, self).delete(*args,**kwargs)
sumber
queryset.delete()
tidak akan membersihkan file dengan solusi ini. Anda perlu mengulang queryset dan memanggil.delete()
setiap objek.Solusi Django 2.x:
Sangat mudah menangani penghapusan file di Django 2 . Saya telah mencoba mengikuti solusi menggunakan Django 2 dan Penyimpanan SFTP dan juga PENYIMPANAN FTP, dan saya cukup yakin bahwa ini akan bekerja dengan manajer penyimpanan lain yang menerapkan
delete
metode. (delete
metode adalah salah satustorage
metode abstrak.)Ganti
delete
metode model sedemikian rupa sehingga instance menghapus FileFields sebelum menghapusnya sendiri:class Song(models.Model): name = models.CharField(blank=True, max_length=100) author = models.ForeignKey(User, to_field='id', related_name="id_user2") song = models.FileField(upload_to='/songs/') image = models.ImageField(upload_to='/pictures/', blank=True) date_upload = models.DateField(auto_now_add=True) def delete(self, using=None, keep_parents=False): self.song.storage.delete(self.song.name) self.image.storage.delete(self.song.name) super().delete()
Ini bekerja sangat mudah bagi saya. Jika Anda ingin memeriksa apakah file sudah ada sebelum dihapus, Anda dapat menggunakan
storage.exists
. mis.self.song.storage.exists(self.song.name)
akan mengembalikanboolean
representasi jika lagu tersebut ada. Jadi akan terlihat seperti ini:def delete(self, using=None, keep_parents=False): # assuming that you use same storage for all files in this model: storage = self.song.storage if storage.exists(self.song.name): storage.delete(self.song.name) if storage.exists(self.image.name): storage.delete(self.song.name) super().delete()
EDIT (Sebagai Tambahan):
Seperti yang disebutkan @HeyMan , dengan solusi ini, pemanggilan
Song.objects.all().delete()
tidak menghapus file! Ini terjadi karenaSong.objects.all().delete()
menjalankan query delete dari Default Manager . Jadi, jika Anda ingin menghapus file model dengan menggunakanobjects
metode, Anda harus menulis dan menggunakan Manajer Kustom (hanya untuk menimpa kueri penghapusannya):class CustomManager(models.Manager): def delete(self): for obj in self.get_queryset(): obj.delete()
dan untuk menetapkan
CustomManager
ke model, Anda harus melakukan inisialobjects
di dalam model Anda:class Song(models.Model): name = models.CharField(blank=True, max_length=100) author = models.ForeignKey(User, to_field='id', related_name="id_user2") song = models.FileField(upload_to='/songs/') image = models.ImageField(upload_to='/pictures/', blank=True) date_upload = models.DateField(auto_now_add=True) objects = CustomManager() # just add this line of code inside of your model def delete(self, using=None, keep_parents=False): self.song.storage.delete(self.song.name) self.image.storage.delete(self.song.name) super().delete()
Sekarang Anda dapat menggunakan
.delete()
di akhirobjects
sub-kueri apa pun . Saya menulis yang paling sederhanaCustomManager
, tetapi Anda dapat melakukannya lebih baik dengan mengembalikan sesuatu tentang objek yang Anda hapus atau apa pun yang Anda inginkan.sumber
Berikut adalah aplikasi yang akan menghapus file lama setiap kali model dihapus atau file baru diunggah: django-smartfields
from django.db import models from smartfields import fields class Song(models.Model): song = fields.FileField(upload_to='/songs/') image = fields.ImageField(upload_to='/pictures/', blank=True)
sumber
@Anton Strogono
Saya kehilangan sesuatu dalam kode ketika file berubah, jika Anda membuat file baru menghasilkan kesalahan, karena file baru tidak menemukan jalan. Saya memodifikasi kode fungsi dan menambahkan kalimat coba / kecuali dan berfungsi dengan baik.
@receiver(models.signals.pre_save, sender=MediaFile) def auto_delete_file_on_change(sender, instance, **kwargs): """Deletes file from filesystem when corresponding `MediaFile` object is changed. """ if not instance.pk: return False try: old_file = MediaFile.objects.get(pk=instance.pk).file except MediaFile.DoesNotExist: return False new_file = instance.file if not old_file == new_file: try: if os.path.isfile(old_file.path): os.remove(old_file.path) except Exception: return False
sumber
try:
blok Anda , meskipun (AttributeError
mungkin?).Kode ini akan berjalan setiap kali saya mengunggah gambar baru (bidang logo) dan memeriksa apakah logo sudah ada, jika demikian, tutup dan hapus dari disk. Prosedur yang sama tentu saja dapat dilakukan dalam fungsi penerima. Semoga ini membantu.
# Returns the file path with a folder named by the company under /media/uploads def logo_file_path(instance, filename): company_instance = Company.objects.get(pk=instance.pk) if company_instance.logo: logo = company_instance.logo if logo.file: if os.path.isfile(logo.path): logo.file.close() os.remove(logo.path) return 'uploads/{0}/{1}'.format(instance.name.lower(), filename) class Company(models.Model): name = models.CharField(_("Company"), null=False, blank=False, unique=True, max_length=100) logo = models.ImageField(upload_to=logo_file_path, default='')
sumber