Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

Django Heroku S3 django-storages のファイルpathについて

参照サイト

File storage API | Django documentation | Django

django+heroku+s3で本番環境を整えていたところ、ファイルアップ関連で以下のエラーが出てしまった。

  raise NotImplementedError("This backend doesn't support absolute paths.")
NotImplementedError: This backend doesn't support absolute paths.

これはどうも、django-storageと、デフォのstorageが競合しているらしい。下の記事に書いてあった。

Solving django-storage NotImplementedError with Amazon S3 | Hardly Code Blog

The conflict was in the s3boto storage class the path method was not implemented. As per the Django documentation, for non local storage you should not implement this method.

とうも、ドキュメントによると、non localな環境だったら、デフォのstorageは使わないほうがいいらしいということ。

The only way I found to solve this solution was to go ahead and implement this method in the django-storage code and modify storages.backends.s3boto and add the path method.

  1. django-storage 、storages.backends.s3boto で実装し直す
  2. django-storageに、path methodを追加する

ただ、この方法はややレベル高めな気がするので、以下の方法を試してみたい。

path -> nameに変える

Resize thumbnails django Heroku, 'backend doesn't support absolute paths' - Stack Overflow

image.save(self.project_thumbnail.name)

元々のコード例:

image.save(self.project_thumbnail.path)

うーん、全くうまくいかない。ただ、さっきの、NotImplementedErrorは解消された。 新たにsuch a file does not exist みたいなerrorが出たので、多分path関連の設定がlocalとはかなり違ってくる模様。

再度原因を考えてみる。

以下がエラーが出ているコードの概要

def create_image(media_path, instance, owner_slug):
    filename = os.path.basename(media_path)
    basename, file_extension = filename.split(".")
    thumb = Image.open(media_path)
    gray_img = thumb.convert('L')
    temp_loc = "%s/%s/tmp" %(settings.MEDIA_ROOT, owner_slug)
    if not os.path.exists(temp_loc):
        os.makedirs(temp_loc)
    temp_file_path = os.path.join(temp_loc, filename)
    if os.path.exists(temp_file_path):
        temp_path = os.path.join(temp_loc, "%s" %(random.random()))
        os.makedirs(temp_path)
        temp_file_path = os.path.join(temp_path, filename)

    temp_image = open(temp_file_path, "w")
    gray_img.save(temp_image,'JPEG')# そのうちちょっと変える。
    thumb_data = open(temp_file_path, "rb")

    thumb_file = File(thumb_data)
    instance.image.save(filename, thumb_file)
    instance.save()


def image_original_post_save_receiver(sender, instance, *args, **kwargs):
    if instance.image:
        obj = Image.objects.all().filter(original__id=instance.id)
        print(obj)
        if obj:
            obj.first().delete()
        processed_obj = MonochromeProcessed.objects.create(original=instance)
        owner_slug = instance.slug
        media_path = instance.image.path
        create_monochrome_image(media_path, processed_obj, owner_slug)

保存した画像を、post_saveのタイミングで、別のデータベースにグレースケールに変換して保存するというコード。

ローカルだとうまくいくけど、クラウドストレージだとpath関連をうまく設定できずに、ファイル参照に失敗して、errorが出てしまうという状態。

nameと、storage.pathを使ってみる。

Solving django-storage NotImplementedError with Amazon S3 | Hardly Code Blog

ここのサイトのコメント欄に有益ぽい情報を発見!以下のようにすると、pathが通るかもとのこと!

Instead of:
photo = Image.open(self.image.path)

Use:
from django.core.files.storage import default_storage as storage
photo = Image.open(storage.open(self.image.name))

ポイント

  • default_storageをstorageとしてimportする
  • image.name としている点(pathだとerrorが出る。絶対pathはlocalのみと覚えておこう)

これでとりあえず、NotImplementedErrorと、pathが変で、開けないというerrorは解消された!

新たなerror OSError: [Errno 30] Read-only file system: ‘/static_in_env’が出てきた。これはおそらく、os.makedir(temp_loc)の箇所で出てきているerror。

新たなerror OSError: [Errno 30] Read-only file system: ‘/static_in_env’について考えてみる。

どうも、そもそも、S3にはフォルダとかディレクトリという概念が無い模様。 そのため、単純にnameを与えれば、それに従ってフォルダとかぽいものは作ってくれるということか??

Amazon S3 boto - how to create a folder? - Stack Overflow

settings.MEDIA_ROOT -> settings.MEDIA_URL で一発で問題解決!

MEDIA_ROOTをsettingsでセットしてなかったので、それをsettings.MEDIA_URLに変更したところ一発でerrorが出なくなった!

temp_loc = "%s/%s/tmp" %(settings.MEDIA_ROOT, owner_slug) 

これを以下のように変更

temp_loc = "%s/%s/tmp" %(settings.MEDIA_URL, owner_slug)

これで、S3のbucketに無事リンクされて保存された!