Django ImageField用相同的名称覆盖图像文件
我有modelDjango ImageField用相同的名称覆盖图像文件,django,django-models,Django,Django Models,我有modelUserProfile和fieldavatar=models.ImageField(upload\u to=upload\u avatar) upload\u avatar函数根据user.id(例如12.png)命名图像文件 但当用户更新化身时,新的化身名称与旧的化身名称一致,Django在文件名中添加后缀(例如12-1.png) 有一种方法可以覆盖文件而不是创建新文件?您可以尝试定义自己的文件系统存储并覆盖默认的get\u availbale\u name方法 from dja
UserProfile
和fieldavatar=models.ImageField(upload\u to=upload\u avatar)
upload\u avatar
函数根据user.id
(例如12.png)命名图像文件
但当用户更新化身时,新的化身名称与旧的化身名称一致,Django在文件名中添加后缀(例如12-1.png)
有一种方法可以覆盖文件而不是创建新文件?您可以尝试定义自己的文件系统存储并覆盖默认的get\u availbale\u 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)
希望这能有所帮助。是的,我也想到了这个。这就是我所做的 型号:
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')
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')
在单独的文件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
显然,这些是示例值,但总的来说,这对我来说很好,并且根据需要修改应该非常简单。嗯。。。这听起来可能有点离经叛道,但目前我的解决方案是检查并删除回调中的现有文件,我已经使用回调来提供上传文件的名称。在models.py中:
def image_path(instance, filename):
return os.path.join('some_dir', str(instance.some_identifier), 'filename.ext')
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(FileSystemStorage):
def get_available_name(self, name, max_length=None):
self.delete(name)
return name
基本上,这将覆盖函数
get\u available\u name
,删除已存在的文件,并返回已存储文件的名称只需参考模型图像字段,将其删除并再次保存
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
model.image.delete()
model.image.save()
我尝试了这里提到的解决方案。但在django 1.10上似乎不起作用。它将在管理员模板的某个位置引发以下错误:
url()缺少1个必需的位置参数:“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)
对于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
谢谢,这是我需要的。也许你知道当用户清除他们的头像时如何删除图像文件吗?@Marco默认情况下,如果文件存在,django不会覆盖该文件。但是,更好的方法是使用
self.delete
,使用它您不需要检查self.exists
,它会忽略在检查文件是否存在和实际删除之间删除文件(由另一个线程或进程)时所产生的错误。在我看来,您应该覆盖文件删除的_save,如以下用户2732686的回答所述,该回答存在严重缺陷。如果存储配置为将文件存储在不同于MEDIA\u ROOT
的目录中,该怎么办?你甚至可以删除一个完全不同的文件。正如其他人指出的,最好使用self.delete(name)
。因为它被标记为正确答案,所以如果它被更正会更好,因为它会被复制粘贴很多。这到现在仍然有用。从Django 2.1实现此功能的任何人都应该添加max_length=None。因此,def get\u available\u name(self,name,max\u length=None)self.delete
将检查文件是否存在,因此无需进行self.exists
检查。最后返回super().get\u available\u name(name,max\u length)
将允许文件名缩短算法工作,如果name
比max\u length
长,你为什么不投票给这个答案呢?在我看来,重写保存以删除文件比在保存方法之前只调用一次get\u available\u name要好得多。。。如果你出于某种原因在代码中的其他地方使用它,它可能会导致bug…@Kukosk这个答案没有更多的更新,主要是因为它晚了3年,可能是因为它缺少描述。FWIW我宁愿不要覆盖父类的内部方法。如果删除了_save()或更改了其参数,则此代码将中断,并且此更改不会在发行说明中提及,因为它不是公共API的一部分。请小心此解决方案。两个进程同时保存同一文件的争用条件可能会触发基类FileSystemStorage.\u save()
中的无限循环。如果在调用基类的\u save()
方法时文件存在,它将捕获产生的EEXIST
异常,并在调用get\u available\u name()
后重试。但是,由于上面已覆盖get\u available\u name()
以不修改文件名,因此将再次发生EEXIST
异常。这个序列被无限重复。@Coderji最终使用了这个解决方案,它确保原始文件在get\u available\u name()
中被删除。您可以将日期时间添加到实际图像中。。你可以用它作为后缀。。比如“2016_987890_image.jpg”。。这将有助于您搜索图像,这里有一个完整的代码示例:这对我很有用。这个解决方案简单明了。