DjangoからAzureストレージにBLOBを保存したときのメモ

django-storagesにAzure用Storageクラスが用意されているので使ってみる。

参考:Azure Storage ― django-storages 1.1.8 documentation

DEFAULT_FILE_STORAGE = 'storages.backends.azure_storage.AzureStorage'
AZURE_ACCOUNT_NAME = ""
AZURE_ACCOUNT_KEY =  ""
AZURE_CONTAINER = ""

適当にImageFieldを持つモデルを用意

class Item(models.Model):
    name = models.CharField(max_length=255)
    photo = models.ImageField(upload_to='item_photo',
                              width_field='width',
                              height_field='height')
    width = models.IntegerField()
    height = models.IntegerField()

ファイル保存のテストクラス

class FileSaveTest(TestCase):
    def test_save(self):
        from items.models import Item

        item = Item(name='dummy')
        image_file = ImageFile(
            open("/path/to/dummy.png", "r"),
            "dummy.png")
        item.photo.save('dummy.png', image_file, save=False)
        item.save()

        self.assertEquals(Item.objects.count(), 1)
        self.assertTrue(Item.objects.get().photo.storage.exists("item_photo/dummy.png"))


実行するとこのようなエラーになった

TypeError: 'NoneType' object has no attribute '__getitem__'

スタックトレースを見ると、保存したファイルのwidth、heightを取得する処理でエラーになっている模様


azureの管理画面を見てみると、BLOBの保存には成功しているが、サイズが9Bなので明らかにおかしい。
'dummy.png'の9文字で9 byteっぽい



ソースを追っかけて調べたところ、azureのクライアントライブラリはBLOBとしてbyte列を受け取る仕様になっているのに対し、django-storageのAzureStorageはFileを渡している。

azureのクライアントライブラリはbyte列でない、byte列にencodeできない入力は無理やりstr(request_body)で評価した結果を保存するのでこのような結果になったようだ。


これじゃ使えないだろ、と思ってリポジトリ調べたらやはりFileからreadしたデータを使うようにプルリクエストがでていた。
david / django-storages / Pull request #76: Fixed Azure backend. url is absolute. Content is stored correctly. ― Bitbucket


プルリクエストを参考にして修正したStorageクラスを使ったら、BLOBが保存できたのでとりあえずめでたしめでたし。