Django filefield.open() 在数据迁移期间访问本地存储而不是远程存储为什么?

问题描述

使用 Django 2.2 和 python 3.7,部署在 Heroku 上

我有一个带有灵活存储位置的文件字段的模型,具体取决于应用环境(开发中的本地文件系统,生产中的 S3)。

我们需要文件上传模型来做更多的事情,我们正在接近它

  1. 创建新模型
  2. 使用数据迁移将数据从旧模型复制到新模型
  3. 删除旧模型

数据迁移在本地(使用本地文件系统存储)运行良好,但在 Heroku 测试服务器(文件存储在 S3 存储桶中)上失败。我收到如下错误消息: FileNotFoundError: [Errno 2] No such file or directory: '/app/private/recalls/user_74/PDIR_BBUY__9e8c995f-512f-446a-9e99-e95f0de1a4ff.pdf' 相关代码如下

现有的“旧”模型:

from django.db import models

from myapp.storage_backends import storages_location,upload_file_rename

class OldUpload(models.Model):
    recall_file = models.FileField(
        validators=[FileExtensionValidator(allowed_extensions=['pdf'],message=RECALL_FILE_EXTENSION_MESSAGE)],storage=storages_location(),upload_to=recall_file_rename
)

myapp.storage_backends(基于 Local filesystem as a remote storage in Django):

import os
import uuid

from storages.backends.s3boto3 import S3Boto3Storage

from django.conf import settings
from django.core.files.storage import FileSystemStorage,get_storage_class

class PrivateS3MediaStorage(S3Boto3Storage):
    location = settings.PRIVATE_MEDIA_LOCATION
    default_acl = 'private'
    file_overwrite = True
    custom_domain = False
    bucket_name = settings.AWS_MEDIA_BUCKET_NAME

class PrivateFileSystemStorage(FileSystemStorage):
    location = @R_404[email protected](settings.MEDIA_ROOT,settings.PRIVATE_MEDIA_LOCATION)

def storages_location():
    media_storage = None
    if settings.SITE_NAME == 'LOCAL':
        base_url = @R_404[email protected](settings.MEDIA_URL.rstrip('/'),settings.PRIVATE_MEDIA_LOCATION)
        PrivateFileSystemStorage(base_url=base_url)
    else:
        media_storage = PrivateS3MediaStorage()
    return media_storage


def upload_file_rename(instance,filename):
    """
    Makes file names unique and splits into directories based on user id.
    """
    base_name,extension = @R_404[email protected](filename)

    filename = '{}__{}'.format(base_name,uuid.uuid4())
    return @R_404[email protected](
        u'recalls',u'user_{}'.format(instance.user.pk),u'{filename}{ext}'.format(filename=filename,ext=extension)
)

新模型:

from django.db import models

from constrainedfilefield.fields import ConstrainedFileField

from myapp.storage_backends import storages_location,upload_file_rename

class NewUpload(models.model):
    uploaded_file = ConstrainedFileField(
        blank=True,null=True,upload_to=upload_file_rename,content_types=ALLOWED_FILE_CONTENT_TYPES,max_upload_size=settings.MAX_UPLOAD_LIMIT
   )

数据迁移:

class Migration(migrations.Migration):
    dependencies = [...]

    def copy_recall_notice_to_upload(apps,schema_editor):
        from django.core.files import File
      
        oldupload_model = apps.get_model("accounts","oldupload")
        newupload_model = apps.get_model("accounts","newupload")
        current_uploads = oldupload_model.objects.all()

        for old_upload in current_uploads:
           new_instance = newupload_model()
           ...(copy other fields)...
           new_file = File(old_upload.recall_file.open(),name='filename')
           new_instance.uploaded_file = new_file
           new_instance.save()

     def reverse(apps,schema_editor):
         ...

     operations = [
        migrations.RunPython(copy_recall_notice_to_upload,reverse)
    ]

当迁移在 Heroku 服务器上运行时,old_upload.recall_file.open() 试图打开本地文件,而不是 S3 上的文件错误信息: FileNotFoundError: [Errno 2] No such file or directory: '/app/private/recalls/user_74/PDIR_BBUY__9e8c995f-512f-446a-9e99-e95f0de1a4ff.pdf'

旧表中的文件不是很多,因此最简单的方法可能是为文件创建新表,然后在迁移后使用 shell 脚本复制文件。不过,我想知道为什么外部存储在迁移中不起作用。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)