简体   繁体   中英

Django - Delete FOLDER from amazon S3

I'm using django post_delete signals to delete all files or folders (folders that would be empty after files be deleted) related to a deleted object in my DB. For some reason I am not able to make the s3 delete a FOLDER, even after searching numerous guides and snippets, maybe someone here knows. I use django 3.2.7 and boto3. Here's my code:

  • models.py
from django.db import models


class Album(models.Model):
    """
    Category Model
    Attributes:
        *name: A string indicating the category name;
        *friendly_name: A string indicating category friendly name.
    Sub-classes:
        *Meta: Display the object's plural name.
    Methods:
        *__str__: Display the object's headline in the admin interface,
                  it returns a nice, human-readable representation of
                  the model;
        *get_friendly_name: Display the object's friendly name.
    """

    category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.SET_NULL)
    title = models.CharField(null=True, max_length=150)
    pre_desc = models.CharField(null=True, max_length=500)
    date = models.DateField(null=True)
    place = models.CharField(null=True, max_length=500)
    cover = models.ImageField(blank=True, null=True, upload_to="portfolio/covers/")
    description = models.TextField(null=True)

    def save(self, *args, **kwargs):
        """
        Override the original save method to set the lineitem total
        and update the order total.
        """
        album_type = self.category.name
        folder_type = album_type.replace(" ", "_")
        album_title = self.title
        folder_name = album_title.replace(" ", "_")

        for field in self._meta.fields:
            if field.name == 'cover':
                field.upload_to = f"portfolio/covers/{folder_type}/{folder_name}"
        super(Album, self).save()

    def __str__(self):
        return self.title


class AlbumPhoto(models.Model):
    """
    Category Model
    Attributes:
        *name: A string indicating the category name;
        *friendly_name: A string indicating category friendly name.
    Sub-classes:
        *Meta: Display the object's plural name.
    Methods:
        *__str__: Display the object's headline in the admin interface,
                  it returns a nice, human-readable representation of
                  the model;
        *get_friendly_name: Display the object's friendly name.
    """

    album = models.ForeignKey(Album, default=None, on_delete=models.CASCADE, related_name='lineitems')
    photos = models.ImageField(upload_to="portfolio/albums/")
    position = models.IntegerField(null=True)

    def save(self, *args, **kwargs):
        """
        Override the original save method to set the lineitem total
        and update the order total.
        """
        album_type = self.album.category.name
        folder_type = album_type.replace(" ", "_")
        album_title = self.album.title
        folder_name = album_title.replace(" ", "_")

        for field in self._meta.fields:
            if field.name == 'photos':
                field.upload_to = f"portfolio/albums/{folder_type}/{folder_name}"
        super(AlbumPhoto, self).save()

    def __str__(self):
        return self.album.title


class Category(models.Model):

    class Meta:
        verbose_name_plural = 'Categories'

    name = models.CharField(max_length=254)
    friendly_name = models.CharField(max_length=254, null=True, blank=True)

    def __str__(self):
        return self.name

    def get_friendly_name(self):
        return self.friendly_name

  • signals.py
import os
import shutil
from django.db.models.signals import post_delete, pre_save
from django.dispatch import receiver
from .models import Album
if os.path.exists('env.py'):
    import env


@receiver(post_delete, sender=Album)
def update_on_delete(sender, instance, **kwargs):
    """
    Deletes file from filesystem
    when corresponding `Album` object is deleted.
    """

    if instance.cover:
        if 'DEVELOPMENT' in os.environ:
            if os.path.isfile(instance.cover.path):
                 #Delete folder from directory
                path_dirs = str(instance.cover.path).split(os.sep)
                del path_dirs[-1]
                shutil.rmtree("/".join(path_dirs))
        else:
            #Delete file from s3
            instance.cover.delete(save=False)

I faced a similar issue and used a recursive call to remove directories and directory contents, something like this may work for your needs.

from django.core.files.storage import default_storage    
        
@receiver(post_delete, sender=Album)
def update_on_delete(sender, instance, **kwargs):
    """ Recursively remove directories and their contents """

        def rm(directory):
            dirs, files = default_storage.listdir(directory)
            for file in files:
                # Delete all files in the current directory
                default_storage.delete(f'{directory}{file}')
            for dir in dirs:
                # Change to next directory; recursive call to delete all files in this directory.
                purge(f'{directory}{dir}/')
    
        rm(f'/The/Root/Dir/Here')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM