जब मैं किसी ऑब्जेक्ट को डेटाबेस / मॉडल से हटाता हूं तो मुझे फ़ाइलों को हटाने के लिए Django एडमिन कैसे मिलेगा?


85

मैं एक मानक ImageField के साथ 1.2.5 का उपयोग कर रहा हूं और अंतर्निहित भंडारण बैकेंड का उपयोग कर रहा हूं। फ़ाइलें ठीक अपलोड होती हैं, लेकिन जब मैं व्यवस्थापक से प्रविष्टि हटाता हूं तो सर्वर पर वास्तविक फ़ाइल नहीं हटती है।


हम्म, वास्तव में यह होना चाहिए। अपने अपलोड फ़ोल्डर (0777 में परिवर्तन) पर फ़ाइल अनुमतियों की जाँच करें।
टॉर्स्टन एंगेलब्रैच

5
Django ने स्वचालित हटाने की सुविधा को हटा दिया (Googlers के लिए जो उपरोक्त टिप्पणी देखते हैं)।
मार्क

जवाबों:


100

आप pre_deleteया post_deleteसंकेत प्राप्त कर सकते हैं (नीचे @ toto_tico की टिप्पणी देखें) और फ़ाइलफ़िल्ट ऑब्जेक्ट पर डिलीट () विधि को कॉल करें , इस प्रकार (मॉडल थिंकपैड में):

class MyModel(models.Model):
    file = models.FileField()
    ...

# Receive the pre_delete signal and delete the file associated with the model instance.
from django.db.models.signals import pre_delete
from django.dispatch.dispatcher import receiver

@receiver(pre_delete, sender=MyModel)
def mymodel_delete(sender, instance, **kwargs):
    # Pass false so FileField doesn't save the model.
    instance.file.delete(False)

10
यदि instance.fileफ़ील्ड रिक्त नहीं है, तो चेक को जोड़ना सुनिश्चित करें या यह संपूर्ण MEDIA_ROOT निर्देशिका को हटाने के लिए (कम से कम प्रयास) कर सकता है। यह ImageField(null=False)खेतों पर भी लागू होता है ।
एंटनी हैचकिंस

47
धन्यवाद। सामान्य तौर पर, मैं post_deleteसंकेत का उपयोग करने की सिफारिश करूंगा क्योंकि यह किसी भी कारण से हटाए जाने की स्थिति में सुरक्षित है। तब न तो मॉडल, न ही डेटा को सुसंगत रखते हुए फ़ाइल को हटाया जाएगा। अगर मेरी post_deleteऔर pre_deleteसंकेतों की समझ गलत है तो कृपया मुझे सुधारें ।
टोटो_टिको

9
ध्यान दें कि यदि आप फ़ाइल को मॉडल उदाहरण पर प्रतिस्थापित करते हैं तो यह पुरानी फ़ाइल को नहीं हटाता है
मार्क

3
यह मेरे लिए Django 1.8 में व्यवस्थापक के बाहर काम नहीं करता है। क्या इसे करने का कोई नया तरीका है?
खिलाफत

बहुत बढ़िया। यह लंबे समय से देख रहा था
RL Shyam

46

Django- सफाई का प्रयास करें

pip install django-cleanup

settings.py

INSTALLED_APPS = (
    ...
    'django_cleanup', # should go after your apps
)

1
बहुत ही कमाल का पैकेज। धन्यवाद! :)
BoJack Horseman

3
सीमित परीक्षण के बाद, मैं पुष्टि कर सकता हूं कि यह पैकेज अभी भी Django 1.10 के लिए काम करता है।
कोडरगुय123

1
अच्छा है, यह इतना आसान है
ट्यून

अच्छा लगा। मेरे लिए Django 2.0 पर काम करता है। मैं अपने स्टोरेज बैकेंड ( django-storages.readthedocs.io/en/latest/backends/… ) के रूप में भी S3 का उपयोग कर रहा हूं और यह S3 से फ़ाइलों को ख़ुशी से हटा रहा है।
रूबेनबर्न

35

Django 1.5 समाधान: मैं विभिन्न कारणों से post_delete का उपयोग करता हूं जो मेरे ऐप के लिए आंतरिक हैं।

from django.db.models.signals import post_delete
from django.dispatch import receiver

@receiver(post_delete, sender=Photo)
def photo_post_delete_handler(sender, **kwargs):
    photo = kwargs['instance']
    storage, path = photo.original_image.storage, photo.original_image.path
    storage.delete(path)

मैंने इसे मॉडल-थ्रेड फ़ाइल के निचले भाग में अटका दिया है।

original_imageक्षेत्र है ImageFieldमेरी में Photoमॉडल।


7
अमेज़ॅन S3 को स्टोरेज बैकेंड (django-storages के माध्यम से) का उपयोग करने वाले किसी के लिए, यह विशेष उत्तर काम नहीं करेगा। आपको फ़ाइल फ़ील्ड के पथ के बजाय NotImplementedError: This backend doesn't support absolute paths.फ़ाइल फ़ील्ड के नाम को पास करके आप इसे आसानी से ठीक कर सकते हैं storage.delete()। उदाहरण के लिए, इस उत्तर की अंतिम दो पंक्तियों को storage, name = photo.original_image.storage, photo.original_image.nameतब से बदल दें storage.delete(name)
सीन अज़लिन

2
@ सीन +1, मैं उस समायोजन का उपयोग 1.7 में कर रहा हूँ ताकि django-imagekit द्वारा S3 पर django-storages के माध्यम से उत्पन्न थंबनेल को हटाया जा सके। docs.djangoproject.com/en/dev/ref/files/storage/... । नोट: यदि आप बस एक ImageField (या FileField) का उपयोग कर रहे हैं, तो आप mymodel.myimagefield.delete(save=False)इसके बजाय उपयोग कर सकते हैं । docs.djangoproject.com/en/dev/ref/files/file/…
user2616836

@ user2616836 आप mymodel.myimagefield.delete(save=False)पर उपयोग कर सकते हैं post_delete? दूसरे शब्दों में, मैं देख सकता हूं कि मैं फाइल को डिलीट कर सकता हूं, लेकिन क्या आप उस फाइल को डिलीट कर सकते हैं, जिसमें इमेजफील्ड वाला मॉडल डिलीट हो जाता है?
यूजीन

1
@ यूजीन आप कर सकते हैं, यह काम करता है (मुझे यकीन नहीं है कि हालांकि क्यों है)। में post_deleteआप करते हैं instance.myimagefield.delete(save=False), के उपयोग पर ध्यान दें instance
user2616836

17

यह कोड Django 1.4 पर भी व्यवस्थापक पैनल के साथ अच्छी तरह से चलता है।

class ImageModel(models.Model):
    image = ImageField(...)

    def delete(self, *args, **kwargs):
        # You have to prepare what you need before delete the model
        storage, path = self.image.storage, self.image.path
        # Delete the model before the file
        super(ImageModel, self).delete(*args, **kwargs)
        # Delete the file after the model
        storage.delete(path)

मॉडल को हटाने से पहले भंडारण और पथ प्राप्त करना महत्वपूर्ण है या बाद में हटाए जाने पर भी शून्य बना रहेगा।


3
यह मेरे लिए काम नहीं करता है (Django 1.5) और Django 1.3 CHANGELOG बताता है: "Django 1.3 में, जब किसी मॉडल को फ़ाइलफिल्ड को हटा दिया जाता है () विधि नहीं कहा जाएगा। यदि आपको अनाथ फ़ाइलों की सफाई की आवश्यकता है, तो आप '। आपको इसे स्वयं संभालने की आवश्यकता होगी (उदाहरण के लिए, एक कस्टम प्रबंधन कमांड के साथ जिसे मैन्युअल रूप से चलाया जा सकता है या उदाहरण के लिए समय-समय पर ccp के माध्यम से चलाया जा सकता है)। "
darrinm

4
यह समाधान गलत है! deleteजब पंक्ति को हटा दिया जाता है तो आपको हमेशा नहीं बुलाया जाता है, आपको संकेतों का उपयोग करना चाहिए।
लावेला

11

आप की जरूरत है दोनों पर वास्तविक फ़ाइल को हटाने के लिए deleteऔर update

from django.db import models

class MyImageModel(models.Model):
    image = models.ImageField(upload_to='images')

    def remove_on_image_update(self):
        try:
            # is the object in the database yet?
            obj = MyImageModel.objects.get(id=self.id)
        except MyImageModel.DoesNotExist:
            # object is not in db, nothing to worry about
            return
        # is the save due to an update of the actual image file?
        if obj.image and self.image and obj.image != self.image:
            # delete the old image file from the storage in favor of the new file
            obj.image.delete()

    def delete(self, *args, **kwargs):
        # object is being removed from db, remove the file from storage first
        self.image.delete()
        return super(MyImageModel, self).delete(*args, **kwargs)

    def save(self, *args, **kwargs):
        # object is possibly being updated, if so, clean up.
        self.remove_on_image_update()
        return super(MyImageModel, self).save(*args, **kwargs)

महान समाधान!
एलेक्सके

6

आप पूर्व-संकेत या पोस्ट_डेली सिग्नल का उपयोग करने पर विचार कर सकते हैं:

https://docs.djangoproject.com/en/dev/topics/signals/

बेशक, उन्हीं कारणों से जो FileField के स्वचालित विलोपन को हटा दिया गया था, वे भी यहां लागू होते हैं। यदि आप किसी ऐसी फ़ाइल को हटाते हैं जिसे कहीं और संदर्भित किया जाता है तो आपको समस्याएँ होंगी।

मेरे मामले में यह उचित लग रहा था क्योंकि मेरे पास अपनी सभी फाइलों को प्रबंधित करने के लिए एक समर्पित फाइल मॉडल था।

नोट: किसी कारण से post_delete सही काम नहीं करता है। फ़ाइल नष्ट हो गई, लेकिन डेटाबेस रिकॉर्ड रुका रहा, जो कि मैं त्रुटि की स्थिति में भी, जो मैं उम्मीद करता हूं, उससे पूरी तरह विपरीत है। pre_delete ठीक काम करता है, हालांकि।


3
शायद post_deleteकाम नहीं करेगा, क्योंकि file_field.delete()डिफ़ॉल्ट रूप से db को मॉडल बचाता है, file_field.delete(False) docs.djangoproject.com/en/1.3/ref/models/fields/…
Adam Jurczyk

3

शायद थोड़ी देर हो जाए। लेकिन मेरे लिए सबसे आसान तरीका एक पोस्ट_सैव सिग्नल का उपयोग करना है। बस यह याद रखने के लिए कि क्वेरीसेट डिलीट प्रक्रिया के दौरान भी सिग्नल एक्ससेक्ट किए जाते हैं, लेकिन [मॉडल] .delete () विधि क्वेरीस डिलीट प्रक्रिया के दौरान एक्ससेक्ट नहीं की जाती है, इसलिए इसे ओवरराइड करना सबसे अच्छा विकल्प नहीं है।

कोर / models.py:

from django.db import models
from django.db.models.signals import post_delete
from core.signals import delete_image_slide
SLIDE1_IMGS = 'slide1_imgs/'

class Slide1(models.Model):
    title = models.CharField(max_length = 200)
    description = models.CharField(max_length = 200)
    image = models.ImageField(upload_to = SLIDE1_IMGS, null = True, blank = True)
    video_embed = models.TextField(null = True, blank = True)
    enabled = models.BooleanField(default = True)

"""---------------------------- SLIDE 1 -------------------------------------"""
post_delete.connect(delete_image_slide, Slide1)
"""--------------------------------------------------------------------------"""

कोर / signals.py

import os

def delete_image_slide(sender, **kwargs):
    slide = kwargs.get('instance')
    try:
        os.remove(slide.image.path)
    except:
        pass

1

यह कार्यक्षमता Django 1.3 में हटा दी जाएगी ताकि मैं इस पर भरोसा न करूं।

आप deleteपूरी तरह से डेटाबेस से प्रविष्टि को हटाने से पहले फ़ाइल को हटाने के लिए मॉडल की विधि को ओवरराइड कर सकते हैं ।

संपादित करें:

यहाँ एक त्वरित उदाहरण है।

class MyModel(models.Model):

    self.somefile = models.FileField(...)

    def delete(self, *args, **kwargs):
        somefile.delete()

        super(MyModel, self).delete(*args, **kwargs)

क्या आपके पास एक उदाहरण है कि फ़ाइल को हटाने के लिए एक मॉडल में इसका उपयोग कैसे करें? मैं डॉक्स को देख रहा हूं और डेटाबेस से ऑब्जेक्ट को हटाने के तरीके के उदाहरणों को देखता हूं लेकिन फ़ाइल हटाने पर कोई कार्यान्वयन नहीं देखता।
narkeeso

2
यह तरीका गलत है क्योंकि यह बल्क डिलीट के लिए काम नहीं करेगा (जैसे एडमिन का 'डिलीट सेलेक्टेड फीचर')। उदाहरण के लिए MyModel.objects.all()[0].delete()फ़ाइल को हटा देगा, जबकि MyModel.objects.all().delete()नहीं करेगा। संकेतों का उपयोग करें।
एंटनी हैचकिंस

1

Post_delete का उपयोग सुनिश्चित करने के लिए जाने के लिए सही तरीका है। कभी-कभी हालांकि चीजें गलत हो सकती हैं, और फाइलें नष्ट नहीं होती हैं। निश्चित रूप से ऐसा मामला है कि आपके पास पुरानी फ़ाइलों का एक गुच्छा है जो पोस्ट_डेलीट के उपयोग से पहले हटाए नहीं गए थे। मैंने एक फ़ंक्शन बनाया जो ऑब्जेक्ट के आधार पर फ़ाइलों को हटाता है यदि फ़ाइल ऑब्जेक्ट संदर्भ मौजूद नहीं है, तो ऑब्जेक्ट हटाएं, यदि फ़ाइल में ऑब्जेक्ट नहीं है, तो भी हटाएं, यह भी एक "सक्रिय" ध्वज के आधार पर हटा सकता है वस्तु .. कुछ मैंने अपने अधिकांश मॉडलों में जोड़ा। आपको इसे उन वस्तुओं को पास करना होगा जिन्हें आप जांचना चाहते हैं, ऑब्जेक्ट फ़ाइलों का पथ, फ़ाइल फ़ील्ड और निष्क्रिय ऑब्जेक्ट को हटाने के लिए एक ध्वज:

def cleanup_model_objects(m_objects, model_path, file_field='image', clear_inactive=False):
    # PART 1 ------------------------- INVALID OBJECTS
    #Creates photo_file list based on photo path, takes all files there
    model_path_list = os.listdir(model_path)

    #Gets photo image path for each photo object
    model_files = list()
    invalid_files = list()
    valid_files = list()
    for obj in m_objects:

        exec("f = ntpath.basename(obj." + file_field + ".path)")  # select the appropriate file/image field

        model_files.append(f)  # Checks for valid and invalid objects (using file path)
        if f not in model_path_list:
            invalid_files.append(f)
            obj.delete()
        else:
            valid_files.append(f)

    print "Total objects", len(model_files)
    print "Valid objects:", len(valid_files)
    print "Objects without file deleted:", len(invalid_files)

    # PART 2 ------------------------- INVALID FILES
    print "Files in model file path:", len(model_path_list)

    #Checks for valid and invalid files
    invalid_files = list()
    valid_files = list()
    for f in model_path_list:
        if f not in model_files:
            invalid_files.append(f)
        else:
            valid_files.append(f)
    print "Valid files:", len(valid_files)
    print "Files without model object to delete:", len(invalid_files)

    for f in invalid_files:
        os.unlink(os.path.join(model_path, f))

    # PART 3 ------------------------- INACTIVE PHOTOS
    if clear_inactive:
        #inactive_photos = Photo.objects.filter(active=False)
        inactive_objects = m_objects.filter(active=False)
        print "Inactive Objects to Delete:", inactive_objects.count()
        for obj in inactive_objects:
            obj.delete()
    print "Done cleaning model."

इसका उपयोग आप इस प्रकार कर सकते हैं:

photos = Photo.objects.all()
photos_path, tail = ntpath.split(photos[0].image.path)  # Gets dir of photos path, this may be different for you
print "Photos -------------->"
cleanup_model_objects(photos, photos_path, file_field='image', clear_inactive=False)  # image file is default

0

सुनिश्चित करें कि आपने फ़ाइल से पहले " स्व " लिखा है । इसलिए ऊपर का उदाहरण होना चाहिए

def delete(self, *args, **kwargs):
        self.somefile.delete()

        super(MyModel, self).delete(*args, **kwargs)

मैं अपनी फ़ाइल से पहले "स्वयं" भूल गया हूं और यह काम नहीं किया क्योंकि यह वैश्विक नामस्थान में दिख रहा था।


0

यदि आपके पास पहले से ही अप्रयुक्त फ़ाइलों की संख्या आपके प्रोजेक्ट में है और उन्हें हटाना चाहते हैं, तो आप django उपयोगिता django-unused-media का उपयोग कर सकते हैं


0

Django 2.x समाधान:

कोई पैकेज स्थापित करने की आवश्यकता नहीं है! Django 2 में संभालना बहुत आसान है । मैंने Django 2 और SFTP संग्रहण का उपयोग करके निम्नलिखित समाधान की कोशिश की है (हालांकि मुझे लगता है कि यह किसी भी स्टोरेज के साथ काम करेगा)

पहले एक कस्टम प्रबंधक लिखें । तो अगर आप का उपयोग करके एक मॉडल की फ़ाइलों को हटाने के लिए सक्षम होना चाहता हूँ objectsतरीकों, आप लिख सकते हैं और उपयोग करना आवश्यक है एक [कस्टम प्रबंधक] [3] (ओवरराइड करने के लिए delete()की विधि objects):

class CustomManager(models.Manager):
    def delete(self):
        for obj in self.get_queryset():
            obj.delete()

अब आपको imageमॉडल को हटाने से पहले हटाना होगा और मॉडल को असाइन करने के CustomManagerलिए, आपको objectsअपने मॉडल के अंदर आरंभ करना होगा :

class MyModel(models.Model):
    image = models.ImageField(upload_to='/pictures/', blank=True)
    objects = CustomManager() # add CustomManager to model
    def delete(self, using=None, keep_parents=False):

    objects = CustomManager() # just add this line of code inside of your model

    def delete(self, using=None, keep_parents=False):
        self.image.storage.delete(self.song.name)
        super().delete()

-1

मेरे पास एक विशेष मामला हो सकता है क्योंकि मैं डायनामिक निर्देशिका नामों के साथ अपने फ़ाइल फ़ील्ड पर upload_to विकल्प का उपयोग कर रहा हूं, लेकिन मुझे जो समाधान मिला वह os.rmdir का उपयोग करना था।

मॉडल में:

import os

...

class Some_Model(models.Model):
     save_path = models.CharField(max_length=50)
     ...
     def delete(self, *args,**kwargs):
          os.rmdir(os.path.join(settings.MEDIA_ROOT, self.save_path)
          super(Some_Model,self).delete(*args, **kwargs)

1
यह बहुत बुरा विचार है। न केवल आप एक एकल फ़ाइल बनाम एक पूरी निर्देशिका (संभावित रूप से अन्य फ़ाइलों को प्रभावित करने वाले) को हटा देंगे, यदि वास्तविक ऑब्जेक्ट विलोपन विफल हो जाता है तो भी आप ऐसा करेंगे।
tbm

यह एक बुरा विचार नहीं है यदि आप मेरे द्वारा की गई समस्या पर काम कर रहे थे;) जैसा कि मैंने उल्लेख किया है कि मेरे पास एक अनूठा उपयोग का मामला था जहां हटाए जा रहे मॉडल का मूल मॉडल था। बच्चों ने मूल फ़ोल्डर में फाइलें लिखी हैं और इसलिए यदि आपने माता-पिता को हटा दिया है तो वांछित व्यवहार यह है कि फ़ोल्डर की सभी फाइलें हटा दी गई हैं। संचालन के क्रम पर अच्छा बिंदु। उस समय मेरे साथ ऐसा नहीं हुआ था।
carruthd

मैं तब भी व्यक्तिगत बच्चे की फाइलों को हटाना पसंद करूंगा जब बच्चा हटा दिया जाएगा; फिर अगर आपको जरूरत है तो खाली होने पर मूल निर्देशिका को हटा सकते हैं।
tbm

यह समझ में आता है कि जब आप बाल वस्तुओं को निकाल रहे होते हैं, लेकिन अगर माता-पिता की वस्तु नष्ट हो जाती है, तो एक समय में बच्चों के साथ कदम रखना थकाऊ और अनावश्यक लगता है। बावजूद, अब मैं देखता हूं कि मैंने जो उत्तर दिया वह ओपी प्रश्न के लिए पर्याप्त नहीं था। टिप्पणियों के लिए धन्यवाद, आपने मुझे कम ब्लंट इंस्ट्रूमेंट का उपयोग करने के बारे में सोचा।
कारुथद
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.