django - type - ImageField sobrescribe el archivo de imagen con el mismo nombre
type models django (8)
Intenté las soluciones mencionadas aquí. Pero parece que no funciona en django 1.10. Se generaría el siguiente error en alguna parte de la plantilla del administrador:
url() missing 1 required positional argument: ''name''
Así que se me ocurrió mi propia solución, que consiste en crear una señal pre_save que intente obtener la instancia de la base de datos antes de que se guarde y eliminarla.
from django.db.models.signals import pre_save
@receiver(pre_save, sender=Attachment)
def attachment_file_update(sender, **kwargs):
attachment = kwargs[''instance'']
# As it was not yet saved, we get the instance from DB with
# the old file name to delete it. Which won''t happen if it''s a new instance
if attachment.id:
attachment = Attachment.objects.get(pk=attachment.id)
storage, path = attachment.its_file.storage, attachment.its_file.path
storage.delete(path)
Tengo el modelo UserProfile
con el campo avatar = models.ImageField(upload_to=upload_avatar)
archivo de imagen de nombres de función user.id
según user.id
(12.png por ejemplo).
Pero cuando el usuario actualiza el avatar, el nuevo nombre del avatar coincide con el antiguo nombre del avatar y Django agrega el sufijo al nombre del archivo (12-1.png, por ejemplo).
¿Hay forma de sobrescribir el archivo en lugar de crear un nuevo archivo ?
Para Django 1.10 encontré que tenía que modificar la respuesta superior para incluir el argumento max_length en la función:
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
Puede escribir la clase de almacenamiento aún mejor de esta manera:
class OverwriteStorage(FileSystemStorage):
def get_available_name(self, name, max_length=None):
self.delete(name)
return name
Básicamente, esto sobrescribirá la función get_available_name
para eliminar el archivo si ya existe y devolver el nombre del archivo ya almacenado.
Puede tratar de definir su propio sistema de archivos y reemplazar el método predeterminado 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
Para su imagen, podría definir un fs como este:
fs = MyFileSystemStorage(base_url=''/your/url/'',
location=''/var/www/vhosts/domain/file/path/'')
avatar = models.ImageField(upload_to=upload_avatar, storage=fs)
Espero que esto ayude.
Sí, esto también me ha venido a mí. Esto es lo que hice.
Modelo:
from app.storage import OverwriteStorage
class Thing(models.Model):
image = models.ImageField(max_length=SOME_CONST, storage=OverwriteStorage(), upload_to=image_path)
También se define en models.py:
def image_path(instance, filename):
return os.path.join(''some_dir'', str(instance.some_identifier), ''filename.ext'')
En un archivo separado, 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
Obviamente, estos son valores de muestra aquí, pero en general, esto funciona bien para mí y esto debería ser bastante sencillo de modificar según sea necesario.
Simplemente refiera su campo de imagen modelo, elimínelo y vuelva a guardarlo.
model.image.delete()
model.image.save()
ejem ... puede sonar poco ortodoxo, pero mi solución, en este momento, es verificar y eliminar el archivo existente dentro de la devolución de llamada que ya uso para proporcionar el nombre del archivo cargado. En 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'')
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