web-dev-qa-db-fra.com

ImageField remplace le fichier image portant le même nom

J'ai le modèle UserProfile avec le champ avatar = models.ImageField(upload_to=upload_avatar)

upload_avatar nomme le fichier image en fonction de user.id (12.png par exemple). 

Mais lorsque l'utilisateur met à jour l'avatar, le nouveau nom d'avatar coïncide avec l'ancien nom d'avatar et Django ajoute un suffixe au nom du fichier (12-1.png par exemple). 

Il y a moyen de écraser le fichier au lieu de créer un nouveau fichier ?

39
Deadly

Ouais, ça m'est arrivé aussi. Voici ce que j'ai fait.

Modèle:

from app.storage import OverwriteStorage

class Thing(models.Model):
    image = models.ImageField(max_length=SOME_CONST, storage=OverwriteStorage(), upload_to=image_path)

Également défini dans models.py:

def image_path(instance, filename):
    return os.path.join('some_dir', str(instance.some_identifier), 'filename.ext')

Dans un fichier séparé, storage.py:

from Django.core.files.storage import FileSystemStorage
from Django.conf import settings
import os

class OverwriteStorage(FileSystemStorage):

    def get_available_name(self, name):
        """Returns a filename that's free on the target storage system, and
        available for new content to be written to.

        Found at http://djangosnippets.org/snippets/976/

        This file storage solves overwrite on upload problem. Another
        proposed solution was to override the save method on the model
        like so (from https://code.djangoproject.com/ticket/11663):

        def save(self, *args, **kwargs):
            try:
                this = MyModelName.objects.get(id=self.id)
                if this.MyImageFieldName != self.MyImageFieldName:
                    this.MyImageFieldName.delete()
            except: pass
            super(MyModelName, self).save(*args, **kwargs)
        """
        # If the filename already exists, remove it as if it was a true file system
        if self.exists(name):
            os.remove(os.path.join(settings.MEDIA_ROOT, name))
        return name

Évidemment, ce sont des exemples de valeurs ici, mais globalement, cela fonctionne bien pour moi et cela devrait être assez simple à modifier si nécessaire.

58
Lockjaw
class OverwriteStorage(get_storage_class()):

    def _save(self, name, content):
        self.delete(name)
        return super(OverwriteStorage, self)._save(name, content)

    def get_available_name(self, name):
        return name
16
user2732686

ahem ... il peut sembler peu orthodoxe, mais ma solution, à l'heure actuelle, est de vérifier et supprimer le fichier existant dans le rappel que j'utilise déjà pour fournir le nom du fichier téléchargé. Dans models.py:

import os
from Django.conf import settings

def avatar_file_name(instance, filename):
    imgname = 'whatever.xyz'
    fullname = os.path.join(settings.MEDIA_ROOT, imgname)
    if os.path.exists(fullname):
        os.remove(fullname)
    return imgname
class UserProfile(models.Model):
    avatar = models.ImageField(upload_to=avatar_file_name,
                                default=IMGNOPIC, verbose_name='avatar')
10
Pynchia

Vous pouvez écrire encore mieux la classe de stockage de cette façon:

class OverwriteStorage(FileSystemStorage):

    def get_available_name(self, name, max_length=None):
        self.delete(name)
        return name

En gros, cela écrasera la fonction get_available_name pour supprimer le fichier s'il existe déjà et renvoyer le nom du fichier déjà stocké.

9
cheap_grayhat

Vous pouvez essayer de définir votre propre Filesystemstorage et de remplacer la méthode par défaut get_availbale_name.

from Django.core.files.storage import FileSystemStorage 
import os

class MyFileSystemStorage(FileSystemStorage):
    def get_available_name(self, name):
        if os.path.exists(self.path(name)):
            os.remove(self.path(name))
        return name

Pour votre image, vous pouvez définir un fs comme ceci:

fs = MyFileSystemStorage(base_url='/your/url/', 
     location='/var/www/vhosts/domain/file/path/')
avatar = models.ImageField(upload_to=upload_avatar, storage=fs)

J'espère que cela t'aides.

6
Jingo

Il suffit de référencer le champ de votre image modèle, de le supprimer et de le sauvegarder à nouveau.

model.image.delete()
model.image.save()
4
Gal Fridman

Pour Django 1.10, j'ai trouvé que je devais modifier la première réponse pour inclure l'argument max_length dans la fonction

from Django.core.files.storage import FileSystemStorage
import os

class OverwriteStorage(FileSystemStorage):
def get_available_name(self, name, max_length=None):
    if self.exists(name):
        os.remove(os.path.join(settings.MEDIA_ROOT, name))
    return name
3
Xeteskian