Django FileField को हटाएं


91

मैं Django में एक वेब ऐप बना रहा हूं। मेरे पास एक मॉडल है जो एक फ़ाइल अपलोड करता है, लेकिन मैं इसे हटा नहीं सकता। यहाँ मेरा कोड है:

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)

फिर, "अजगर प्रबंधन ओरेकल शेल" में मैं यह करता हूं:

song = Song.objects.get(pk=1)
song.delete()

यह डेटाबेस से डिलीट हो जाता है लेकिन सर्वर पर फाइल नहीं। मेरे द्वारा और क्या आजमाया जा सकता है?

धन्यवाद!


सीधे default_storage का उपयोग करने के बारे में क्या? docs.djangoproject.com/en/dev/topics/files
MGP

जवाबों:


141

Django 1.3 से पहले, जब आप संबंधित मॉडल उदाहरण को हटाते हैं, तो फ़ाइल स्वचालित रूप से फाइलसिस्टम से हटा दी जाती थी। आप शायद एक नया Django संस्करण का उपयोग कर रहे हैं, इसलिए आपको फ़ाइल सिस्टम से फ़ाइल को हटाने का कार्यान्वयन स्वयं करना होगा।

आप कुछ तरीकों से ऐसा कर सकते हैं, जिनमें से एक pre_deleteया post_deleteसंकेत का उपयोग कर रहा है।

उदाहरण

मेरी पसंद का तरीका वर्तमान में post_deleteऔर pre_saveसंकेतों का मिश्रण है , जो इसे बनाता है ताकि अप्रचलित फ़ाइलों को हटा दिया जाए जब भी संबंधित मॉडल हटाए जाते हैं या उनकी फाइलें बदल जाती हैं।

एक काल्पनिक MediaFileमॉडल पर आधारित :

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()(जैसे बल्क अपडेट करके a QuerySet) मॉडल फ़ाइल को इंगित करता है , तो पुरानी फ़ाइल आस-पास पड़ी रहेगी क्योंकि सिग्नल नहीं चलेंगे। यदि आप पारंपरिक फ़ाइल हैंडलिंग विधियों का उपयोग करते हैं तो ऐसा नहीं होता है।
  • मुझे लगता है कि मैंने जिन ऐप्स का निर्माण किया है उनमें से एक में यह कोड उत्पादन में है लेकिन फिर भी अपने जोखिम पर उपयोग करें।
  • कोडिंग शैली: यह उदाहरण fileफ़ील्ड नाम के रूप में उपयोग करता है , जो एक अच्छी शैली नहीं है क्योंकि यह अंतर्निहित fileऑब्जेक्ट पहचानकर्ता के साथ टकराता है ।

यह सभी देखें

  • FieldFile.delete()Django में 1.11 मॉडल फ़ील्ड संदर्भ (ध्यान दें कि यह FieldFileवर्ग का वर्णन करता है , लेकिन आप .delete()सीधे फ़ील्ड पर कॉल करेंगे : FileFieldउदाहरण संबंधित FieldFileउदाहरण के लिए, और आप इसके तरीकों तक पहुँचते हैं जैसे कि वे फ़ील्ड के हैं)

    ध्यान दें कि जब कोई मॉडल हटा दिया जाता है, तो संबंधित फाइलें नष्ट नहीं होती हैं। यदि आपको अनाथ फ़ाइलों को साफ़ करने की आवश्यकता है, तो आपको इसे स्वयं हैंडल करने की आवश्यकता होगी (उदाहरण के लिए, कस्टम प्रबंधन आदेश के साथ जो मैन्युअल रूप से चलाया जा सकता है या उदाहरण के लिए क्रोन के माध्यम से समय-समय पर चलाया जा सकता है)।

  • Django स्वचालित रूप से फ़ाइलों को क्यों नहीं हटाता है: Django 1.3 के लिए रिलीज़ नोटों में प्रविष्टि

    पहले के Django वर्जन में, जब एक मॉडल इंस्टेंस FileFieldको डिलीट किया गया था, तो FileFieldइसे बैकएंड स्टोरेज से फाइल को डिलीट करने के लिए खुद ही ले लिया। इसने कई डेटा-लॉस परिदृश्यों के लिए दरवाजा खोल दिया, जिसमें एक ही फाइल को संदर्भित करने वाले विभिन्न मॉडलों पर लुढ़के हुए लेनदेन और फ़ील्ड शामिल हैं। Django 1.3 में, जब एक मॉडल हटा दी जाती है FileFieldकी delete()विधि कहा जाता है नहीं किया जाएगा। यदि आपको अनाथ फ़ाइलों की सफाई की आवश्यकता है, तो आपको इसे स्वयं हैंडल करने की आवश्यकता होगी (उदाहरण के लिए, एक कस्टम प्रबंधन कमांड जिसे मैन्युअल रूप से चलाया जा सकता है या उदाहरण के लिए क्रोन के माध्यम से समय-समय पर चलाने के लिए)।

  • pre_deleteकेवल एक संकेत का उपयोग करने का उदाहरण


2
हां, लेकिन उचित जांच अवश्य कराएं। (मुझे एक सेकंड दें, मैं वास्तविक प्रणाली में उपयोग में पाया गया कोड पोस्ट करूंगा।)
एंटोन स्ट्रोगनॉफ

7
इसका उपयोग करना बेहतर है instance.song.delete(save=False), क्योंकि यह सही django स्टोरेज इंजन का उपयोग करता है।
एडुआर्डो

1
आजकल यह है कि मैं कोड की नकल करता हूं मैं खुद को SO से सीधे नहीं लिख पा रहा हूं और यह सीमित संशोधनों के साथ काम करता है। शानदार मदद, धन्यवाद!
GJStein

इसमें एक बग मिला जहां यदि उदाहरण मौजूद है, लेकिन कोई छवि पहले से सहेजी नहीं गई थी, तो os.path.isfile(old_file.path)विफल हो जाती है क्योंकि old_file.pathकोई त्रुटि उठाता है (कोई फ़ाइल फ़ील्ड से संबद्ध नहीं है)। मैंने इसे if old_file:कॉल करने से ठीक पहले जोड़कर ठीक किया os.path.isfile()
तीन_पिनएप्पल्स

@three_pineapples समझ में आता है। यह हो सकता है कि फ़ाइल फ़ील्ड पर पूर्ण नल बाधा को बाईपास नहीं किया गया था या किसी बिंदु पर बाहर नहीं निकला था, उस स्थिति में कुछ ऑब्जेक्ट इसे खाली कर देंगे।
एंटोन स्ट्रोगोनॉफ

77

Django-cleanup का प्रयास करें , जब आप मॉडल को हटाते हैं तो यह स्वचालित रूप से FileField पर डिलीट विधि को आमंत्रित करता है।

pip install django-cleanup

settings.py

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

कूल, इसे डिफ़ॉल्ट रूप से FileField में जोड़ना होगा, धन्यवाद!
मगाजो

यह अपलोड करते समय फ़ाइल को डिलीट कर रहा है
चिराग सोनी

वाह। मैं यह नहीं होने के लिए कोशिश कर रहा था और मैं यह पता नहीं लगा सका कि यह क्यों था। किसी ने इस साल पहले स्थापित किया था और इसके बारे में भूल गया था। धन्यवाद।
ryan28561

4
तो, Django ने पहली बार में फ़ाइलफ़ील्ड डिलीट फ़ंक्शन को क्यों हटाया?
हा-नेउल

आप किंवदंती हैं !!
मार्लनजेड

31

आप .deleteDjango> = 1.10 के साथ नीचे दिखाए गए फ़ाइल फ़ील्ड की कॉलिंग विधि के साथ फ़ाइल सिस्टम से फ़ाइल को हटा सकते हैं :

obj = Song.objects.get(pk=1)
obj.song.delete()

6
स्वीकृत उत्तर होना चाहिए, सरल और सिर्फ काम करता है।
निकोले शिंदारोव

14

यदि आप मौजूद हैं और सुपर फ़ंक्शन को कॉल करने से पहले इसे हटा सकते हैं, तो आप फ़ाइल के लिए जाँच करने के लिए मॉडल के डिलीट फंक्शन को अधिलेखित कर सकते हैं।

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)

8
खबरदार कि कॉलिंग queryset.delete()इस समाधान के साथ फ़ाइलों को साफ नहीं करेगी। आपको .delete()प्रत्येक ऑब्जेक्ट पर क्वेरीसेट और कॉल करने की आवश्यकता होगी ।
स्कॉट वुडल

मैं Django के लिए नया हूँ। यह अच्छा है, लेकिन क्या होगा अगर मॉडल एक अमूर्त वर्ग से विरासत में मिला था, जिसने डिलीट विधि को खत्म कर दिया है, क्या यह उस सार वर्ग से ओवरराइड नहीं करेगा? संकेतों का उपयोग करना मेरे लिए बेहतर प्रतीत होता है
theTypan

8

Django 2.x समाधान:

Django 2 में फ़ाइल विलोपन को संभालना बहुत आसान है । मैंने Django 2 और SFTP स्टोरेज और FTP स्टोरेज का उपयोग करके निम्नलिखित समाधान की कोशिश की है, और मुझे पूरा यकीन है कि यह किसी भी अन्य स्टोरेज मैनेजर के साथ काम करेगा जिसने deleteपद्धति लागू की थी । ( deleteविधि एक हैstorage सार विधियों में से एक है।)

deleteमॉडल की विधि को इस तरह से ओवरराइड करें कि इंस्टेंस खुद को डिलीट करने से पहले उसकी FileFields को हटा दे:

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()

यह मेरे लिए बहुत आसान काम करता है। यदि आप जाँचना चाहते हैं कि फ़ाइल विलोपन से पहले मौजूद है, तो आप उपयोग कर सकते हैं storage.exists। उदाहरण के लिए अगर गीत मौजूद है तो self.song.storage.exists(self.song.name)वापस लौट आएगा boolean। तो यह इस तरह दिखेगा:

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 (परिवर्धन में):

जैसा कि @HeyMan ने उल्लेख किया है, इस समाधान के साथ कॉलिंग Song.objects.all().delete()फ़ाइलों को नष्ट नहीं करती है! यह इसलिए हो रहा है क्योंकि डिफ़ॉल्ट प्रबंधकSong.objects.all().delete() की डिलीट क्वेरी चल रही है । इसलिए यदि आप विधियों का उपयोग करके किसी मॉडल की फ़ाइलों को हटाने में सक्षम होना चाहते हैं , तो आपको एक कस्टम प्रबंधक लिखना होगा और उसका उपयोग करना होगा (बस इसकी डिलीट क्वेरी को ओवरराइड करने के लिए):objects

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

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

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()

अब आप .delete()किसी भी objectsउप-प्रश्न के अंत में उपयोग कर सकते हैं । मैंने सबसे सरल लिखा है CustomManager, लेकिन आप अपने द्वारा हटाई गई वस्तुओं या आप जो भी चाहते हैं उसके बारे में कुछ वापस करके इसे बेहतर कर सकते हैं।


1
हाँ, मुझे लगता है कि मैंने सवाल पोस्ट करने के बाद से उन्होंने वह फीचर जोड़ा है।
मार्कोस अगुआयो

1
अभी भी डिलीट को वेन कॉलिंग सॉन्ग नहीं कहा जाता है। इजेक्टसॉल ()। डिलीट ()। उसी समय जब इंस्टेंस on_delete = मॉडल द्वारा हटा दिया जाता है। CASCADE।
हेमैन

@ हेमैन मैंने इसे हल किया और अभी अपना समाधान संपादित किया :)
हमीद्रेज़ा

4

यहाँ एक ऐसा ऐप है जो मॉडल को डिलीट करने या पुरानी फ़ाइल अपलोड होने पर पुरानी फ़ाइलों को हटा देगा: 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)

3

@ एटन स्ट्रॉगनॉफ

फ़ाइल बदलते समय मुझे कोड में कुछ याद आ रहा है, यदि आप एक नई फ़ाइल बनाते हैं तो एक त्रुटि उत्पन्न होती है, क्योंकि एक नई फ़ाइल एक पथ नहीं मिली है। मैंने फ़ंक्शन के कोड को संशोधित किया और एक कोशिश / वाक्य को छोड़कर इसे जोड़ा और यह अच्छी तरह से काम करता है।

@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

मैंने इसका सामना नहीं किया है - मेरे कोड में बग हो सकता है, या Django में कुछ बदल सकता है। मैं आपके try:ब्लॉक में विशिष्ट अपवाद को पकड़ने का सुझाव दूंगा , हालांकि ( AttributeErrorशायद?)।
एंटोन स्ट्रोगोनॉफ

ओएस लाइब्रेरी का उपयोग करना इतना अच्छा नहीं है, क्योंकि आप विभिन्न स्टोरेज (उदाहरण के लिए अमेज़ॅन एस 3) पर माइग्रेट होने पर समस्याओं का सामना करेंगे।
इगोर पोमरान्स्की

@IgorPomaranskiy जब आप os.remove का उपयोग करते हैं तो अमेजन S3 जैसे स्टोरेज में क्या होगा ??
डैनियल गोंजालेज फर्नांडीज

@ DanielGonzálezFernández मुझे लगता है कि यह विफल होगा (गैर-मौजूदा पथ के बारे में कुछ त्रुटि की तरह)। यही कारण है कि Django स्टोरेज के लिए सार का उपयोग करता है।
इगोर पोमेरान्स्की 10

0

यह कोड हर बार जब मैं एक नई छवि (लोगो फ़ील्ड) अपलोड करता हूं और जांचता हूं कि क्या कोई लोगो पहले से मौजूद है, तो इसे बंद करें और इसे डिस्क से हटा दें। एक ही प्रक्रिया निश्चित रूप से रिसीवर फ़ंक्शन में बनाई जा सकती है। उम्मीद है की यह मदद करेगा।

 #  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='')
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.