我的模型UserProfile
包含字段avatar = models.ImageField(upload_to=upload_avatar)
upload_avatar
函数根据user.id
(例如12.png)命名图像文件。
但是当用户更新头像时,新头像名称与旧头像名称一致,Django将后缀添加到文件名(例如12-1.png)。
有办法覆盖文件而不是创建新文件?
答案 0 :(得分:61)
是的,这也是我的想法。这就是我所做的。
型号:
from app.storage import OverwriteStorage
class Thing(models.Model):
image = models.ImageField(max_length=SOME_CONST, storage=OverwriteStorage(), upload_to=image_path)
还在models.py中定义:
def image_path(instance, filename):
return os.path.join('some_dir', str(instance.some_identifier), 'filename.ext')
在另一个文件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
显然,这些是这里的示例值,但总的来说这对我来说效果很好,而且根据需要进行修改应该非常简单。
答案 1 :(得分:17)
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
答案 2 :(得分:10)
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')
答案 3 :(得分:10)
您可以通过以下方式更好地编写存储类:
class OverwriteStorage(FileSystemStorage):
def get_available_name(self, name, max_length=None):
self.delete(name)
return name
基本上这将覆盖函数get_available_name
以删除文件(如果已存在)并返回已存储文件的名称
答案 4 :(得分:6)
您可以尝试定义自己的Filesystemstorage并覆盖默认的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
对于你的图像你可以定义这样的fs:
fs = MyFileSystemStorage(base_url='/your/url/',
location='/var/www/vhosts/domain/file/path/')
avatar = models.ImageField(upload_to=upload_avatar, storage=fs)
希望这有帮助。
答案 5 :(得分:4)
只需参考模型图像字段,将其删除并再次保存。
model.image.delete()
model.image.save()
答案 6 :(得分:3)
对于Django 1.10,我发现我必须修改最佳答案,在函数中包含max_length参数:
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
答案 7 :(得分:0)
我尝试了这里提到的解决方案。但它似乎不适用于django 1.10。它会在管理员模板的某处产生以下错误:
url() missing 1 required positional argument: 'name'
所以我提出了自己的解决方案,其中包括创建一个pre_save信号,该信号试图在数据库被保存之前从数据库中获取实例并删除它的文件路径:
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)