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が保存できたのでとりあえずめでたしめでたし。