Python 如何更改预保存信号中的models.FileField?

Python 如何更改预保存信号中的models.FileField?,python,django,Python,Django,我有一个定义如下的模型字段: file = models.FileField(storage=S3BotoStorage(bucket=blablabla), upload_to='blablabla', max_length=250) 我想做的是在pre_save中抓取文件,压缩它,然后保存压缩版本 你可能会说我应该在我的视图中这样做,但这是不可能的,因为我使用的是Django管理站点,我需要在整个代码中保存任何文件,无论是来自视图还是Django管理站点 那么,我怎样才能访问上传的文件、压

我有一个定义如下的模型字段:

file = models.FileField(storage=S3BotoStorage(bucket=blablabla), upload_to='blablabla', max_length=250)
我想做的是在
pre_save
中抓取
文件
,压缩它,然后保存压缩版本

你可能会说我应该在我的视图中这样做,但这是不可能的,因为我使用的是Django管理站点,我需要在整个代码中保存任何文件,无论是来自视图还是Django管理站点


那么,我怎样才能访问上传的文件、压缩文件并将其保存而不是原始文件?

在预保存信号期间,文件的真实名称似乎不是最终名称

我要做的是使用post_save信号获取最终名称,并将该名称写入另一个位置(表、文件等)

我还将创建一个crontab作业(可以是python程序或shell脚本),该作业将在上载文件夹上运行,并压缩所有未压缩的文件。
此作业可以根据DB查询(您在其中更新了最终名称)决定压缩哪个文件
它还可以使用“新压缩文件名”更新一些额外字段,并带有一个标志,表明文件已成功压缩)

  • 创建另一个模型来描述文件上载。
    在模型中,每个文件都应至少包含以下字段
    “文件名:字符串”,“压缩文件名:字符串”,“压缩:布尔”
  • 在为当前模型进行后期保存期间,更新此新模型的“文件名”
  • 设置一个cron选项卡,从新模型中读取“was_zipped==none”的所有条目
    这个Crontab作业将压缩所有这些文件,并在写入新文件后更新模型
此解决方案将允许您将“内部管理”与主代码分离。

它还将使“保存”操作更快(保存过程中无压缩)

post\u保存信号中执行此操作更容易,如下所示:

@receiver(post_save, sender=MyModel)
def post_save_MyModel(sender, instance, *args, **kwargs):
  filefield = instance.file
  oldname = filefield.name        #gets the "normal" file name as it was uploaded  
  if not oldname:
    return                          #do nothing if file name is missing (no file was uploaded)

  storage = filefield.storage
  #build a unique file name for the file like: newname = '{}.zip'.format(instance.pk)
  newname = <create a new filename with zip extension here>
  if storage.path(newname) == storage.path(oldname):
    return                             #do nothing if file already renamed (avoid reentrancy)

  content = storage.open(oldname,"rb")
  content = <zip content in-memory here>

  storage.delete(newname)           #delete any file with the new name eventually present already
  #save the file field using the file name without directories
  filefield.save(os.path.basename(newname), content, save=True)   #must save model instance too!!!   
  content.close()                   #must close the original content, before deleting it!!!  
  storage.delete(oldname)           #delete any file with the old name eventually present already
所有这些都适用于“正常”本地文件存储。我不知道S3BOtograge,但是它应该服从存储接口,所以它应该有所有必要的方法

此方法的一个问题可能是,对于S3,文件名将是S3本身指定的某种UUID,因此可能无法为文件使用不同的名称,但也可能根本不需要它


无论如何,要小心检查模型实例重新保存时出现的重新进入状态(如果文件名确实更改,则需要更新文件名)。

感谢cron概念。我用过。
@receiver(post_delete, sender=MyModel)
def post_delete_MyModel(sender, instance, *args, **kwargs):
    filefield = instance.file
    if filefield.name:                  #only work if file actually present
        filefield.delete(save=False)        #do NOT delete the instance!!!