Django: 如何替换/更新/FileField的文件?

13 投票
3 回答
21506 浏览
提问于 2025-04-16 10:32

在Django中,我有一个这样的模型:

from django.db import models
from django.core.files.base import File
import os, os.path

class Project(models.Model):
    video = models.FileField(upload_to="media")

    def replace_video(self):
        """Convert video to WebM format."""
        # This is where the conversion takes place,
        # returning a path to the new converted video
        # that I wish to override the old one.
        video_path = convert_video()

        # Replace old video with new one,
        # and remove original unconverted video and original copy of new video.
        self.video.delete(save=True)
        self.video.save(os.path.basename(video_path), File(open(video_path ,"wb")), save=True)
        os.remove(video_path)

我想要在模型对象/实例的FileField video替换文件。我写的方法没有成功。一旦我删除了原始文件,就会出现以下错误:

ValueError: The 'video' attribute has no file associated with it.

我该如何用更新的文件替换原来的文件,并且删除原来的文件(不再需要)呢?

附注:我找到一个相关的问题,但没有得到令人满意的答案。

3 个回答

0

另一种解决办法是删除原来的模型实例,然后创建一个新的。

比如说:

def upload_metadata(request):
  if request.method == 'POST':
    try: # removes existing file
      uf = UserFile.objects.get(
        library_id = request.POST['library_id'],
        file = 'uploads/' + request.FILES['file']._get_name())
      uf.delete()
    except Exception as e:
      pass
    # ... continue with form handling as before ...
5

我最近也遇到了这个问题,最后是这样解决的:

from django.db import models
from django.core.files.base import File
import os, os.path

class Project(models.Model):
    video = models.FileField(upload_to="media")

    def replace_video(self):
        """Convert video to WebM format."""
        # This is where the conversion takes place,
        # returning a path to the new converted video
        # that I wish to override the old one.
        video_path = convert_video()

        # Replace old video with new one,
        # and remove original unconverted video and original copy of new video.
        old_path = self.video.path
        self.video.save(os.path.basename(video_path), File(open(video_path ,"wb")), save=True)
        os.remove(video_path)
        os.remove(old_path)
12

你有两个选择。

我假设你的 Project 模型只是代码的一小部分。

选择一是把你的模型拆分开来,让一个项目(Project)不再只有一个文件,而是和多个项目文件(ProjectFile)关联。也就是说,一个项目可以有多个项目文件。具体来说,项目文件里有一个外键(ForeignKey)指向项目。

这样你就可以根据旧的项目文件添加新的项目文件。你可以删除它们,随便操作。实际上,你可以保留两个项目文件,并标记哪个是“当前”的。

选择二是用 self.video.open("w") 来打开文件进行写入。直接在原地重写内容。也就是说,不是先删除再替换文件,而是用新内容覆盖旧文件。

with open(video_path ,"rb") as source:
    self.video.open("wb")
    bytes= source.read(4096)
    if bytes: 
        self.video.write( bytes )
        bytes= source.read(4096)

这样做可能就能达到你想要的效果。

是的,这看起来效率不高,但其实并没有那么糟糕。转换过程可能很慢,但复制文件只需要几秒钟。

撰写回答