[PYTHON] ImageField de Django

Préparation

PIL (Pillow) est requis pour gérer ImageField, donc si vous n'êtes pas dans l'environnement d'exécution Python, installez-le ci-dessous:

pip install pillow
pip3 install pillow # Python 3.x

J'ai également besoin de définir MEDIA_ROOT dans settings.py, alors ajoutez-le:

settings.py


MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

Ajoutez également ce qui suit à ʻurls.py`:

urls.py


urlpatterns = [
    url(r'^media/(?P<path>.*)$','django.views.static.serve', {'document_root': settings.MEDIA_ROOT}),
]

Qu'est-ce qu'ImageField?

Différents champs peuvent être définis dans le modèle de Django, mais il existe ** ImageField ** comme champ pour gérer facilement les fichiers image. Voici l'exemple le plus simple:

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

Désormais, lors de l'enregistrement d'un fichier image, il sera enregistré avec MEDIA_ROOT + 'images / (nom du fichier image d'origine). Si le "nom du fichier image d'origine" est couvert, ** Django ajoutera un suffixe étrange et ne l'écrasera pas. ** **

Je ne souhaite pas utiliser le nom de fichier d'origine

Tout d'abord, je voulais enregistrer l'image en utilisant la clé primaire lors de son enregistrement. Par exemple, le nom du fichier d'origine est car.png, mais si la clé primaire pour la sauvegarde est ʻid = 123 (pk = 123) `, elle sera renommée en 123.png.

ImageField peut modifier librement non seulement le répertoire de destination de sauvegarde de l'image, mais aussi le nom du fichier en donnant une fonction à l'argument ʻupload_to`, mais même si vous faites ce qui suit, par exemple, l'id n'a pas encore été pris lorsque la fonction est appelée. Donc ça ne marche pas:

def get_image_path(self, filename):
    prefix = 'images/'
    name = self.id  #Dans ce cas, l'ID n'a pas encore été décidé lors de la création d'un nouveau, il sera donc Aucun.!!Pas bien
    extension = os.path.splitext(filename)[-1]
    return prefix + name + extension

Ensuite, lorsque self.id est None, c'est une façon de prendre max (id) + 1 de DB et de l'utiliser, mais cela dépend de DB, mais l'id suivant n'est pas nécessairement max (id). Il manque de rigueur car il ne devient pas id) + 1.

Selon StackOverFlow, il est dit: "Enregistrez-le avec un nom approprié pour le moment, et si l'ID est décidé après l'enregistrement, vous devez le renommer avec le nom de fichier correct." J'ai également essayé cela, mais le fichier réel est certainement C'est bien, mais le chemin dans la base de données est encore ancien, donc c'est étrange quand je le vois avec l'administrateur Django.

Plus tard, par exemple, si vous l'enregistrez sous 123.png et mettez à jour l'image, vous ne pouvez pas ** écraser 123.png, et ** 123 créera un fichier incompréhensible appelé .png. C'est parce que Django n'autorise pas l'écrasement. Pour faire face à cela, vous devez supprimer l'ancien fichier à l'avance, puis l'enregistrer ...

J'ai renoncé à enregistrer le nom du fichier image avec la clé primaire car cela devenait ennuyeux quand j'y pensais.

Vous devriez le garder comme UUID

Si vous souhaitez créer un nom de fichier normalement unique, vous pouvez utiliser UUID, j'ai donc essayé ce qui suit:

def get_image_path(self, filename):
    """Obtenez un chemin d'image personnalisé.

    :param self:exemple(models.Model)
    :param filename:Nom du fichier d'origine
    :return:Chemin de l'image contenant le nom de fichier personnalisé
    """
    prefix = 'images/'
    name = str(uuid.uuid4()).replace('-', '')
    extension = os.path.splitext(filename)[-1]
    return prefix + name + extension

Maintenant, il est enregistré avec un nom tel que images / 7f9a9970cc8645a99a2191c114856426.jpg. Cependant, même si ImageField est mis à jour ou supprimé sur le site de gestion tel quel, il y a un problème que l'enregistrement DB disparaît mais le fichier d'origine reste. Je veux faire quelque chose à ce sujet.

Supprimer les anciens fichiers image lors de la mise à jour ou de la suppression

Comme le titre l'indique, je veux supprimer les anciens fichiers et les rendre plus propres lorsque save () ou delete (). Il est conseillé d'enregistrer l'ancien chemin du fichier avant de mettre à jour / de supprimer, puis de supprimer l'ancien fichier une fois le traitement effectué. C'est là qu'intervient le décorateur.

def delete_previous_file(function):
    """Implémentation de Decorator pour supprimer les anciens fichiers qui ne sont plus nécessaires.

    :param function:Fonction principale
    :return: wrapper
    """
    def wrapper(*args, **kwargs):
        """Fonction wrapper.

        :param args:Argument arbitraire
        :param kwargs:Argument de mot clé arbitraire
        :return:Résultat d'exécution de la fonction principale
        """
        self = args[0]

        #Obtenez le nom du fichier avant d'enregistrer
        result = Image.objects.filter(pk=self.pk)
        previous = result[0] if len(result) else None
        super(Image, self).save()

        #Exécution de la fonction
        result = function(*args, **kwargs)

        #Supprimer tous les fichiers avant de les enregistrer
        if previous:
            os.remove(MEDIA_ROOT + '/' + previous.image.name)
        return result
    return wrapper

Préparez une fonction pour un tel décorateur,

class Image(models.Model):
    @delete_previous_file
    def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
        super(Image, self).save()

    @delete_previous_file
    def delete(self, using=None, keep_parents=False):
        super(Image, self).delete()

    image = models.ImageField('image', upload_to=get_image_path)

Ce faisant, les anciens fichiers seront supprimés lors de la mise à jour / suppression.

Recommended Posts

ImageField de Django
Utilisation de ImageField de Django avec AppEngine / Python
Django order_by notes
Bibliothèque recommandée de Django
Mémorandum de base de Django
À propos du ProxyModel de Django