developer tip

장고 파일 필드 삭제

copycodes 2020. 10. 28. 08:12
반응형

장고 파일 필드 삭제


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)

그런 다음 "python manage.py shell"에서 다음을 수행합니다.

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

데이터베이스에서는 삭제되지만 서버의 파일은 삭제되지 않습니다. 또 무엇을 시도 할 수 있습니까?

감사!


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()(예 : 대량 업데이트 QuerySet) 신호가 실행되지 않기 때문에 이전 파일이 계속 거짓말을합니다. 일반적인 파일 처리 방법을 사용하는 경우에는 발생하지 않습니다.
  • 내가 만든 앱 중 하나가이 코드를 프로덕션에 가지고 있지만 그럼에도 불구하고 자신의 책임하에 사용한다고 생각합니다.
  • 코딩 스타일 :이 예제는 file기본 제공 file개체 식별자 와 충돌하기 때문에 좋은 스타일이 아닌 필드 이름으로 사용 합니다.

또한보십시오

  • FieldFile.delete()Django 1.11 모델 필드 참조 ( FieldFile클래스를 설명 하지만 .delete()필드에서 직접 호출 합니다. FileField인스턴스는 해당 FieldFile인스턴스에 프록시를 사용하고 필드의 것처럼 메서드에 액세스합니다)

    모델을 삭제해도 관련 파일은 삭제되지 않습니다. 고아 파일을 정리해야하는 경우 직접 처리해야합니다 (예 : 수동으로 실행하거나 예를 들어 cron을 통해 주기적으로 실행하도록 예약 할 수있는 사용자 지정 관리 명령 사용).

  • Django가 파일을 자동으로 삭제하지 않는 이유 : Django 1.3 릴리스 정보 항목

    이전 Django 버전에서는를 포함하는 모델 인스턴스 FileField가 삭제 FileField되었을 때 자체적으로 백엔드 저장소에서 파일을 삭제했습니다. 이로 인해 롤백 된 트랜잭션과 동일한 파일을 참조하는 다른 모델의 필드를 포함하여 여러 데이터 손실 시나리오가 발생했습니다. Django 1.3에서는 모델이 삭제 될 때 FileFielddelete()메서드가 호출되지 않습니다. 분리 된 파일을 정리해야하는 경우 직접 처리해야합니다 (예 : 수동으로 실행하거나 예를 들어 cron을 통해 주기적으로 실행하도록 예약 할 수있는 사용자 지정 관리 명령 사용).

  • pre_delete신호 만 사용하는 예


django-cleanup을 시도 하면 모델을 제거 할 때 FileField에서 delete 메서드를 자동으로 호출합니다.

pip install django-cleanup

settings.py

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

You can also simply overwrite the delete function of the model to check for file if it exists and delete it before calling the super function.

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)

You can delete file from filesystem with calling .delete method of file field shown as below with Django >= 1.10:

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

@Anton Strogonoff

I missing something in the code when a file change, if you create a new file generate an error, becuase is a new file a did not find a path. I modified the code of function and added a try/except sentence and it works well.

@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

Here is an app that will remove old files whenever model is deleted or a new file is uploaded: 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)

This code will run every time i upload a new image (logo field) and check if a logo already exists if so, close it and remove it from disk. The same procedure could of course be made in receiver function. Hope this helps.

 #  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='')

참고URL : https://stackoverflow.com/questions/16041232/django-delete-filefield

반응형