我有一个具有FileField的Django模型,并且在创建 new 对象时,我想使用对象的Id
来命名FileField。
这里是模型(为简洁起见,删除了一些字段)。值得注意的是,FileField
使用对象的ID作为名称来重命名文件,以避免冲突。
class Product(models.Model):
name = models.CharField(max_length=255, db_index=True)
...
storage_path = 'foo/'
image = models.FileField(default="foo/default.png", upload_to=upload_path, blank=True, null=True)
def upload_path(instance, filename):
""" We don't care what filename the user gave us, use the ID. """
_, ext = os.path.splitext(filename)
if hasattr(ext, 'lower'):
ext = ext.lower()
path = getattr(instance, 'storage_path', 'UNCATEGORIZED')
return "{}/{}{}".format(path, instance.id, ext)
但是,在使用CreateView
时,我不能简单地包含image
字段,因为当保存表单(并且正在上传文件)时,还没有创建对象,因此ID为None
。因此,我每次都会节省None.png
。
我有一个可行的解决方案,但这似乎不是很优雅。
class ProductCreate(CreateView):
model = Product
fields = ['name', 'image']
def dispatch(self, request, *args, **kwargs):
# We need to save the image after the object is saved because we
# use the ID in the image filename
self.image_file = request.FILES.pop('image', None)
if isinstance(self.image_file, list) and len(self.image_file) > 0:
self.image_file = self.image_file[0]
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
ret = super().form_valid(form)
if self.image_file:
self.object.image.save(self.image_file.name, self.image_file)
return ret
因此,基本上,我是在form_valid
(或链中较早的内容)可以处理文件之前对其进行拦截,然后稍后再使用该InMemoryUploadedFile
实例。它工作正常,但我担心它会在将来崩溃,并且还有更好的方法。
我敢肯定有更好的解决方案。也许只是不使用文件名中的ID?我当然可以使用UUID,但是我喜欢使用ID命名文件。