はじめに
DRFで配列やファイル含むデータを送信する際に、multipart/form-dataで送信したが、PostManでもうまくいかず、FrontEnd側やPostManの使い方と思いこんでしまいはまってしまいました。私がやったやり方は以下のとおりです。
- multipart/form-dataで送信 ※前提条件のとおりですが、Base64とか他にもやり方はあるそうです
- ファイルはFileFieldを使わない
- ViewSetで、createとupdateをオーバライドする ※Serializerでオーバライドする方法もあるそうですが、よく分かりませんでした。
目次
正解
models.py
from django.db import models
from eqs.models import Eqs, EqTypes
class Infos(models.Model):
titles = models.CharField(max_length=32)
sammary = models.TextField(max_length=100)
text = models.TextField()
def __str__(self):
return self.titles
class Files(models.Model):
"""
ファイル格納用
"""
Infos = models.ForeignKey('Infos', on_delete=models.CASCADE, related_name="files")
filePath = models.CharField(primary_key=True, max_length=512)
def __str__(self):
return self.filePath
serializer.py
from rest_framework import serializers
from .models import Infos, Files
class InfosSerializer(serializers.ModelSerializer):
files = serializers.StringRelatedField(many=True, read_only=True)
class Meta:
model = Infos
fields = "__all__"
serializer.py
import os
from rest_framework import viewsets
from rest_framework import status
from rest_framework.generics import get_object_or_404
from rest_framework.response import Response
from .models import Infos, Files
from .serializer import InfosSerializer
UPLOAD_DIR = 'media/' # mediaフォルダに格納するためのアップロードパス
class InfosViewSet(viewsets.ModelViewSet):
queryset = Infos.objects.all()
serializer_class = InfosSerializer
def create(self, request, *args, **kwargs):
info = Infos.objects.create()
return self.serialize(request, info)
def update(self, request, pk, *args, **kwargs):
info = get_object_or_404(Infos, pk=pk)
return self.serialize(request, info)
def serialize(self, request, bugInfo):
serializers = InfosSerializer(instance=Info, data=request.data, partial=True)
serializers.is_valid(raise_exception=True)
serializers.save()
self.delFiles(request)
self.setFiles(info, request)
return Response(serializers.data, status.HTTP_200_OK)
def setFiles(self, info, request):
files = request.FILES.getlist('addFiles[]')
for file in files:
# ファイル保存処理
path = os.path.join(UPLOAD_DIR, file.name)
destination = open(path, 'wb')
for chunck in file.chunks():
destination.write(chunck)
destination.close
# model保存処理
if not os.path.exists(path):
continue
else:
fileobj = Files.objects.create(filePath=path, infos=info)
fileobj.save()
def delFiles(self, request):
str_delFiles = request.POST.getlist('delFiles[]')
for str_delFile in str_delFiles:
fileobj = Files.objects.get(filePath=str_delFile)
fileobj.delete()
os.remove(str_delFile)
補足
PostManでの確認方法
PostManでの配列の送信方法はkeyの後ろに「[]」を付ければOK(Advanced Rest Clientでも同じ)
axiosでの送信方法
save.js
// 一部抜粋
save() {
const editedId = this.editID;
const formData = new FormData();
Object.entries(this.editedItem).forEach(([key, value]) => {
if (Array.isArray(value)) {
value.forEach((v, i) => {
formData.append(key + '[]', v);
});
} else {
formData.append(key, value);
}
});
try {
if (editedId === -1) {
axios.post('/Info/', formData, config).then(postRes => {
this.editedItem.id = postRes.data;
axios.get('/Info/' + postRes.data.id + '/').then(getRes => {
this.infos.push(getRes.data);
});
});
this.close();
} else {
axios
.put('/Info/' + this.editedItem.id + '/', formData, config)
.then(putRes => {
axios.get('/Info/' + putRes.data.id + '/').then(getRes => {
Object.assign(this.infos[editedId], getRes.data);
});
});
this.close();
}
} catch (error) {
alert('DBに保存されませんでした\n' + error);
}
},