django ImageField覆盖具有相同名称的图像文件

hrysbysz  于 2023-05-01  发布在  Go
关注(0)|答案(9)|浏览(101)

我有模型UserProfile,字段avatar = models.ImageField(upload_to=upload_avatar)
upload_avatar函数根据user.id(12.例如png)。
但是当用户更新头像时,新头像名称与旧头像名称一致,并且Django在文件名中添加后缀(12-1.例如png)。
有办法覆盖文件而不是创建新文件吗?

9rbhqvlz

9rbhqvlz1#

是啊,我也想到了。我是这么做的。
型号:

from app.storage import OverwriteStorage

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

也在www.example中定义 www.example.com

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

在单独的文件www.example中 www.example.com

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

显然,这里的值是示例值,但总的来说,这对我来说很好,而且在必要时修改应该很简单。

rdlzhqv9

rdlzhqv92#

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, max_length=None):
        return name
ukxgm1gy

ukxgm1gy3#

你可以用这种方式更好地编写存储类:

class OverwriteStorage(FileSystemStorage):

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

基本上,这将覆盖函数get_available_name以删除文件(如果文件已存在)并返回已存储文件的名称

41ik7eoe

41ik7eoe4#

只需引用您的模型图像字段,删除它并再次保存。

model.image.delete()
model.image.save()
oewdyzsn

oewdyzsn5#

嗯……这听起来可能不太正统,但我的解决方案,目前,是检查并删除回调中的现有文件,我已经使用它来提供上传文件的名称。Inwww.example. www.example.com

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')
tez616oj

tez616oj6#

对于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
csga3l58

csga3l587#

您可以尝试定义自己的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)

希望这能帮上忙。

sqyvllje

sqyvllje8#

我尝试了这里提到的解决方案。但它似乎在Django 1上不起作用。10.它会在管理员模板的某个地方引发以下错误:
url() missing 1 required positional argument: 'name'
所以我想出了自己的解决方案,它包括创建一个pre_保存信号,试图在保存之前从数据库中获取示例并删除它的文件路径:

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)
mm5n2pyu

mm5n2pyu9#

在Django 4.2您可以尝试定义自己的Filesystemstorage并覆盖默认的exists方法。

class OverwriteStorage(FileSystemStorage):
    def exists(self, name):
        exists = os.path.lexists(self.path(name))
        if exists:
            os.remove(os.path.join(settings.MEDIA_ROOT, name))
        return exists

并使用新的文件系统类定义模型

class IMGUpload(TimeStampMixin):
    file = models.FileField(blank=False, null=False,
                            storage=OverwriteStorage(),
                            upload_to=get_uploaded_file_path,
                            validators=[validate_file_extension])

相关问题