Django帮助函数重命名用户上传的图片



对于一个django项目,我有一个具有图像字段的模型。这个想法是,用户上传一个图像,django根据选择的模式将其重命名,然后将其存储在媒体文件夹中。

为了实现这一点,我在一个单独的文件utils.py中创建了一个助手类。这个类可以在django ImageField模型的upload_to参数中调用。应该通过连接所创建项的.name和.id属性来重命名图像。我的解决方案的问题是,如果图像是在创建新的项目对象时上传的,那么就没有.id值可以使用(目前还没有)。而不是我们说:banana_12.jpg我得到的是banana_none。jpg。如果我将图像上传到已经存在的项目对象上,那么图像将被正确地重命名。这是我的解决方案。我如何改进代码,使其在新项目对象上工作?有更好的方法吗?

# utils.py
from django.utils.deconstruct import deconstructible
import os
@deconstructible
class RenameImage(object):
"""Renames a given image according to pattern"""
def __call__(self, instance, filename):
"""Sets the name of an uploaded image"""
self.name, self.extension = os.path.splitext(filename)
self.folder = instance.__class__.__name__.lower()

image_name = f"{instance}_{instance.id}{self.extension}"
return os.path.join(self.folder, image_name)
rename_image = RenameImage()
# models.py
from .utils import rename_image
class Item(models.Model):
# my model for the Item object
name = models.CharField(max_length=30)
info = models.CharField(max_length=250, blank=True)
image = models.ImageField(upload_to=rename_image, blank=True, null=True) # <-- helper called here
# ...

我创建了助手类RenameImage,由于上面解释的原因,它只有在对象已经存在时才能正常工作。

__编辑

我根据postrongave建议做了一些改进。用户上传的图像被调整大小和重命名为我想要的,但然后我得到一个RecursionError: maximum recursion depth exceeded。似乎通过调用实例上的save()方法,我进入了一个信号循环…有办法解决这个问题吗?

from django.db.models.signals import post_save
from .models import Item
from PIL import Image
import os

def resize_rename(sender, instance, created, max_height=300, max_width=300, **kwargs):
if instance.image: # only fire if there is an image
with open(instance.image.path, 'r+b') as f:
image = Image.open(f)

# Resize the image if larger than max values
if image.height > max_height or image.width > max_width:
output_size = (max_height, max_width)
image.thumbnail(output_size, Image.ANTIALIAS)
image.save(instance.image.path, quality=100)
# Grab the image extension
_name, extension = os.path.splitext(instance.image.name)
# Use old_name to create the desired new_name while keeping the same dirs
old_name = instance.image.name.split('/')[-1] # e.g. IMG_3402.jpg
new_name = instance.image.name.replace(old_name, f"{instance.name}_{instance.id}{extension}")

# Rename if name is not the right one already
if old_name != new_name:

old_path = instance.image.path
new_path = old_path.replace(old_name, f"{instance.name}_{instance.id}{extension}")

instance.image.name = new_name # Assign the new name
os.replace(old_path, new_path) # Replace with the new file
instance.save(update_fields=['image']) # Save the instance
post_save.connect(resize_rename, sender=Item)

这是不可能的,因为id是在保存后分配的。您必须使用postrongave信号,然后更改文件名为此,在app下添加signal.py在apps.py文件中,重写ready函数以添加signals.py import语句

apps.py

from django.apps import AppConfig

class AppNameConfig(AppConfig):
name = 'app_name'
def ready(self):
import app_name.signals

在init.py中设置默认的app配置

init.py

default_app_config = 'app_name.apps.AppNameConfig'

signals.py

from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Item
@receiver(post_save, sender=Item)
def post_save(sender, instance: FilettoPasso, **kwargs):
if kwargs['created']:        
print(instance.id)
instance.image.path = new path
# rename file in os path
...
instance.save()


相关内容

最新更新