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 ?
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.
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
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')
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é.
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.
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()
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