Bagaimana cara mengganti delete() pada suatu model dan membuatnya tetap berfungsi dengan penghapusan terkait

Saya mengalami masalah karena saya menghapus Widget dengan menggunakan some_widget_instance.delete(). Saya juga memiliki model bernama WidgetFile dengan metode override delete() sehingga saya dapat menghapus file dari hard drive saya ketika WidgetFile dihapus. Masalah yang saya alami adalah jika saya menghapus Widget, dan WidgetFiles terkait dengannya seperti ini:

class WidgetFile(models.Model):

    widget = models.ForeignKey(Widget)

Nah, ketika saya menghapus Widget itu, WidgetFiles-nya dihapus tetapi metode delete() tidak terpicu dan melakukan hal-hal tambahan pada hard drive saya. Bantuan apa pun sangat dihargai.


person orokusaki    schedule 08.10.2009    source sumber
comment
Masalah ini muncul karena ketika widget dihapus, widget tersebut tidak memicu metode delete() pada masing-masing dependennya (kelas yang memiliki referensi kunci asing ke dalamnya). Itu hanya menghapus objek terkait dari DB. Hal ini membuatnya lebih efisien tetapi jelas menimbulkan masalah seperti ini.   -  person orokusaki    schedule 17.12.2009


Jawaban (8)


Saya melakukan hal yang sama dan melihat ada nugget di dokumen Django yang harus Anda pikirkan.

Mengganti metode model yang telah ditentukan sebelumnya

Mengganti Hapus Perhatikan bahwa metode delete() untuk suatu objek tidak harus dipanggil saat menghapus objek secara massal menggunakan QuerySet. Untuk memastikan logika penghapusan yang disesuaikan dijalankan, Anda dapat menggunakan sinyal pre_delete dan/atau post_delete.

Ini berarti cuplikan Anda tidak selalu melakukan apa yang Anda inginkan. Menggunakan Sinyal adalah pilihan yang lebih baik untuk menangani penghapusan.

Saya pergi dengan yang berikut ini:

import shutil
from django.db.models.signals import pre_delete 
from django.dispatch import receiver

@receiver(pre_delete)
def delete_repo(sender, instance, **kwargs):
    if sender == Set:
        shutil.rmtree(instance.repo)
person ElementalVoid    schedule 01.10.2012
comment
Apa itu Set? model yang ingin Anda hapus? - person John Wang; 07.11.2019

Saya menemukan jawabannya. Saya baru saja meletakkan ini pada model Widget itu:

def delete(self):
    files = WidgetFile.objects.filter(widget=self)
    if files:
        for file in files:
            file.delete()
    super(Widget, self).delete()

Ini memicu metode delete() yang diperlukan pada setiap objek terkait, sehingga memicu kode penghapusan file khusus saya. Memang databasenya lebih mahal, tapi ketika Anda mencoba menghapus file di hard drive, tidak terlalu mahal untuk menekan db beberapa kali lagi.

person orokusaki    schedule 08.10.2009
comment
Tidak jelas mengapa Anda berpikir Celery atau cron tidak akan menghadapi situasi serupa di mana file yang dihapus mungkin sudah dibuka untuk operasi baca/tulis dari proses lain. Dalam kedua kasus tersebut, Anda harus membuat kode untuk menangani kasus khusus. - person Joseph Paetz; 11.12.2013
comment
Saya menghapus bagian itu... 4,5 tahun yang lalu Saya pikir itu mungkin ide yang bagus, tapi saya tidak yakin mengapa. - person orokusaki; 31.03.2014

Menggunakan clear() sebelum menghapus, menghapus semua objek dari kumpulan objek terkait.

lihat django-following-relationships-backward

contoh:

group.link_set.clear() 
group.delete() 
person panchicore    schedule 08.10.2009

Ini sepertinya masuk akal jika satu Widget terhubung ke satu WidgetFile dengan tepat. Dalam hal ini Anda harus menggunakan OneToOneField

dari Contoh langsung:

# Delete the restaurant; the waiter should also be removed
>>> r = Restaurant.objects.get(pk=1)
>>> r.delete()
person vikingosegundo    schedule 08.10.2009
comment
benar memang, tetapi Django melakukan penghapusan massal tingkat basis data pada semua pelayan tanpa memicu masing-masing metode penghapusannya, yang lebih murah, namun juga kurang konvensional. - person orokusaki; 08.10.2009

Hanya untuk mengatasi masalah ini dengan cara yang mungkin: pra-hapus sinyal. (Sama sekali tidak menyiratkan bahwa tidak ada solusi sebenarnya.)

person che    schedule 08.10.2009


Apakah some_widget_instance dan contoh Widget atau WidgetFile ? Karena jika itu adalah turunan dari Widget maka tidak akan mendapatkan fungsi delete() khusus Anda, yang ada di kelas WidgetFile.

person thornomad    schedule 08.10.2009

Dari Django 1.9, jika Anda mendefinisikan on_delete=models.CASCADE untuk bidang, itu akan menghapus semua objek terkait pada penghapusan.

person CLTanuki    schedule 08.04.2016
comment
Ini tidak benar. Pertanyaannya bukan tentang penghapusan berjenjang. Ini tentang memastikan metode delete terkait dipanggil. - person orokusaki; 09.04.2016