Django Rest Framework를 사용하여 파일을 업로드하고 JSON payload를 전송하려면 어떻게 해야 합니까?
JSON payload뿐만 아니라 파일을 수신할 수 있는 Django Rest Framework API 핸들러를 작성하려고 합니다.MultiPartParser를 핸들러 파서로 설정했습니다.
하지만 둘 다 할 수 없을 것 같아요.파일과 함께 payload를 멀티 파트 요구로 전송하면 request.data에서 JSON payload를 magred 방식으로 사용할 수 있습니다(첫 번째 텍스트 부분부터 첫 번째 콜론까지의 키, 나머지 부분은 데이터).표준 형식의 파라미터로 파라미터를 송신할 수 있습니다만, API의 나머지 부분은 JSON payload를 받아들이기 때문에 일관성을 유지하고 싶었습니다.요청입니다.몸을 일으킬 때 읽을 수 없다*** RawPostDataException: You cannot access body after reading from request's data stream
예를 들어 파일 및 요청 본문의 다음 페이로드가 있습니다.
{"title":"Document Title", "description":"Doc Description"}
이하가 됩니다.
<QueryDict: {u'fileUpload': [<InMemoryUploadedFile: 20150504_115355.jpg (image/jpeg)>, <InMemoryUploadedFile: Front end lead.doc (application/msword)>], u'{%22title%22': [u'"Document Title", "description":"Doc Description"}']}>
방법이 있을까요?케이크를 먹고, 보관하고, 살이 찌지 않게 할 수 있을까요?
편집: 이것은 Django REST Framework 업로드 이미지의 복사본일 수 있습니다: "제출된 데이터는 파일이 아닙니다."그것은 아니다.업로드 및 요청은 멀티파트 방식으로 진행되며, 파일 및 업로드도 문제없으니 유의하시기 바랍니다.표준 양식 변수를 사용하여 요청을 완료할 수도 있습니다.대신 JSON payload를 가져올 수 있는지 알고 싶습니다.
파일을 업로드하여 데이터를 전송해야 하는 사용자에게 파일을 바로 사용할 수 있는 방법은 없습니다.이에 대한 json api 사양에 미해결 문제가 있습니다.제가 본 한 가지 가능성은multipart/related
여기 보이는 바와 같이 drf로 구현하기는 매우 어렵다고 생각합니다.
마지막으로 제가 실행한 것은 요청서를 보내는 것이었습니다.formdata
각 파일을 파일로 전송하고 다른 모든 데이터를 텍스트로 전송합니다.이제 데이터를 텍스트로 전송하기 위해 데이터라는 단일 키를 가지고 전체 json을 값의 문자열로 전송할 수 있습니다.
Models.py
class Posts(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
caption = models.TextField(max_length=1000)
media = models.ImageField(blank=True, default="", upload_to="posts/")
tags = models.ManyToManyField('Tags', related_name='posts')
serializers.py -> 특별한 변경은 필요 없습니다.쓰기 가능한 ManyToMany Field에 의해 시리얼라이저가 너무 길다고 표시되지 않습니다.
views.py
class PostsViewset(viewsets.ModelViewSet):
serializer_class = PostsSerializer
parser_classes = (MultipartJsonParser, parsers.JSONParser)
queryset = Posts.objects.all()
lookup_field = 'id'
json을 해석하려면 다음과 같이 커스텀 파서가 필요합니다.
utils.py
from django.http import QueryDict
import json
from rest_framework import parsers
class MultipartJsonParser(parsers.MultiPartParser):
def parse(self, stream, media_type=None, parser_context=None):
result = super().parse(
stream,
media_type=media_type,
parser_context=parser_context
)
data = {}
# find the data field and parse it
data = json.loads(result.data["data"])
qdict = QueryDict('', mutable=True)
qdict.update(data)
return parsers.DataAndFiles(qdict, result.files)
우체부의 요청 예
편집:
각 데이터를 키 값 쌍으로 전송하려면 이 확장 답변을 참조하십시오.
이게 오래된 이야기라는 건 알지만, 우연히 이걸 발견했어요. 수 ★★★★★★★★★★★★★★★★★★★★★★★.MultiPartParser
을 사용하다
# views.py
class FileUploadView(views.APIView):
parser_classes = (MultiPartParser,)
def put(self, request, filename, format=None):
file_obj = request.data['file']
ftype = request.data['ftype']
caption = request.data['caption']
# ...
# do some stuff with uploaded file
# ...
return Response(status=204)
JS를 사용한 My Angular 코드ng-file-upload
말합니다
file.upload = Upload.upload({
url: "/api/picture/upload/" + file.name,
data: {
file: file,
ftype: 'final',
caption: 'This is an image caption'
}
});
JSON과 제품 오브젝트 작성/업데이트 이미지를 보냅니다.아래는 나에게 적합한 APIView 생성입니다.
시리얼라이저
class ProductCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = [
"id",
"product_name",
"product_description",
"product_price",
]
def create(self,validated_data):
return Product.objects.create(**validated_data)
보다
from rest_framework import generics,status
from rest_framework.parsers import FormParser,MultiPartParser
class ProductCreateAPIView(generics.CreateAPIView):
queryset = Product.objects.all()
serializer_class = ProductCreateSerializer
permission_classes = [IsAdminOrIsSelf,]
parser_classes = (MultiPartParser,FormParser,)
def perform_create(self,serializer,format=None):
owner = self.request.user
if self.request.data.get('image') is not None:
product_image = self.request.data.get('image')
serializer.save(owner=owner,product_image=product_image)
else:
serializer.save(owner=owner)
테스트 예:
def test_product_creation_with_image(self):
url = reverse('products_create_api')
self.client.login(username='testaccount',password='testaccount')
data = {
"product_name" : "Potatoes",
"product_description" : "Amazing Potatoes",
"image" : open("local-filename.jpg","rb")
}
response = self.client.post(url,data)
self.assertEqual(response.status_code,status.HTTP_201_CREATED)
@으로는 문자열로서 @Nithin JSON을 하지 않는 application/json
멀티파트 세그먼트 내부에 있습니다.
우리가 원하는 것은 백엔드가 아래 형식의 데이터를 받아들이도록 하는 것이다.
------WebKitFormBoundaryrga771iuUYap8BB2
Content-Disposition: form-data; name="file"; filename="1x1_noexif.jpeg"
Content-Type: image/jpeg
------WebKitFormBoundaryrga771iuUYap8BB2
Content-Disposition: form-data; name="myjson"; filename="blob"
Content-Type: application/json
{"hello":"world"}
------WebKitFormBoundaryrga771iuUYap8BB2
Content-Disposition: form-data; name="isDownscaled"; filename="blob"
Content-Type: application/json
false
------WebKitFormBoundaryrga771iuUYap8BB2--
MultiPartParser
는 위의 형식으로 동작하지만, 이러한 jsons는 파일로 취급됩니다.는 그 를 unmarshaling으로 을 해제하면 됩니다.data
.
파서화이
from rest_framework import parsers
class MultiPartJSONParser(parsers.MultiPartParser):
def parse(self, stream, *args, **kwargs):
data = super().parse(stream, *args, **kwargs)
# Any 'File' found having application/json as type will be moved to data
mutable_data = data.data.copy()
unmarshaled_blob_names = []
json_parser = parsers.JSONParser()
for name, blob in data.files.items():
if blob.content_type == 'application/json' and name not in data.data:
mutable_data[name] = json_parser.parse(blob)
unmarshaled_blob_names.append(name)
for name in unmarshaled_blob_names:
del data.files[name]
data.data = mutable_data
return data
설정을 지정합니다.화이
REST_FRAMEWORK = {
..
'DEFAULT_PARSER_CLASSES': [
..
'myproject.parsers.MultiPartJSONParser',
],
}
이제 작동해야 합니다.
이치노 이후client
장고 REST는 JSON의 JSON입니다.
import io
import json
def JsonBlob(obj):
stringified = json.dumps(obj)
blob = io.StringIO(stringified)
blob.content_type = 'application/json'
return blob
def test_simple(client, png_3x3):
response = client.post(f'http://localhost/files/', {
'file': png_3x3,
'metadata': JsonBlob({'lens': 'Sigma 35mm'}),
}, format='multipart')
assert response.status_code == 200
Incorrect type. Expected pk value, received list.
@@nithin이 있기 QueryDict
방해가 되고 있습니다. 사전의 각 항목에 대해 목록을 사용하도록 특별히 구성되어 있으므로 다음과 같습니다.
{ "list": [1, 2] }
에 의해 되는 경우MultipartJsonParser
{ 'list': [[1, 2]] }
시리얼라이저가 고장나죠.
은 이으로, '예상하다'를 들 수 있습니다. 특히 이 문제는_data
JSON © : :
from rest_framework import parsers
import json
class MultiPartJSONParser(parsers.MultiPartParser):
def parse(self, stream, *args, **kwargs):
data = super().parse(stream, *args, **kwargs)
json_data_field = data.data.get('_data')
if json_data_field is not None:
parsed = json.loads(json_data_field)
mutable_data = {}
for key, value in parsed.items():
mutable_data[key] = value
mutable_files = {}
for key, value in data.files.items():
if key != '_data':
mutable_files[key] = value
return parsers.DataAndFiles(mutable_data, mutable_files)
json_data_file = data.files.get('_data')
if json_data_file:
parsed = parsers.JSONParser().parse(json_data_file)
mutable_data = {}
for key, value in parsed.items():
mutable_data[key] = value
mutable_files = {}
for key, value in data.files.items():
mutable_files[key] = value
return parsers.DataAndFiles(mutable_data, mutable_files)
return data
멀티파트 투고와 일반 뷰를 사용하는 것은 매우 간단합니다(옵션인 경우).
json을 필드로 보내고 파일을 파일로 보낸 다음 하나의 보기에서 처리합니다.
다음은 간단한 python 클라이언트와 Django 서버입니다.
클라이언트 - 여러 파일과 임의 json 인코딩 개체 전송:
import json
import requests
payload = {
"field1": 1,
"manifest": "special cakes",
"nested": {"arbitrary":1, "object":[1,2,3]},
"hello": "word" }
filenames = ["file1","file2"]
request_files = {}
url="example.com/upload"
for filename in filenames:
request_files[filename] = open(filename, 'rb')
r = requests.post(url, data={'json':json.dumps(payload)}, files=request_files)
서버 - json을 사용하고 파일을 저장합니다.
@csrf_exempt
def upload(request):
if request.method == 'POST':
data = json.loads(request.POST['json'])
try:
manifest = data['manifest']
#process the json data
except KeyError:
HttpResponseServerError("Malformed data!")
dir = os.path.join(settings.MEDIA_ROOT, "uploads")
os.makedirs(dir, exist_ok=True)
for file in request.FILES:
path = os.path.join(dir,file)
if not os.path.exists(path):
save_uploaded_file(path, request.FILES[file])
else:
return HttpResponseNotFound()
return HttpResponse("Got json data")
def save_uploaded_file(path,f):
with open(path, 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
@ 것은 @Pithios의에서 DRF가 어떻게 입니다.utils/html#parse_html_list
class MultiPartJSONParser(parsers.MultiPartParser):
def parse(self, stream, *args, **kwargs):
data = super().parse(stream, *args, **kwargs)
# Any 'File' found having application/json as type will be moved to data
mutable_data = data.data.copy()
unmarshaled_blob_names = []
json_parser = parsers.JSONParser()
for name, blob in data.files.items():
if blob.content_type == 'application/json' and name not in data.data:
parsed = json_parser.parse(blob)
if isinstance(parsed, list):
# need to break it out into [0], [1] etc
for idx, item in enumerate(parsed):
mutable_data[name+f"[{str(idx)}]"] = item
else:
mutable_data[name] = parsed
unmarshaled_blob_names.append(name)
for name in unmarshaled_blob_names:
del data.files[name]
data.data = mutable_data
return data
다음 코드가 나에게 효과가 있었다.
from django.core.files.uploadedfile import SimpleUploadedFile
import requests
from typing import Dict
with open(file_path, 'rb') as f:
file = SimpleUploadedFile('Your-Name', f.read())
data: Dict[str,str]
files: Dict[str,SimpleUploadedFile] = {'model_field_name': file}
requests.put(url, headers=headers, data=data, files=files)
requests.post(url, headers=headers, data=data, files=files)
'model_field_name'
이에요.FileField
★★★★★★★★★★★★★★★★★」ImageField
자료들은 '다보다'로 수 요.name
★★★★★★★★★★★★★★★★★」location
처럼 '아예'를 data
파라미터를 지정합니다.
이게 도움이 됐으면 좋겠다.
이 작업은 다음과 같습니다.
class FileUpload(APIView):
parser_classes = [MultiPartParser]
authentication_classes = [JWTAuthentication]
def post(self, request, filename, format=None):
file = request.data['file']
data = json.loads(request.POST['form'])
#.... just do.....
. . .
프런트 엔드 부품: fetch를 사용한 예(vue 프런트 엔드)
let data = await new FormData(); // creates a new FormData object
data.append("file", this.files); // add your file to form data
data.append('form',JSON.stringify(body)) //add your json
fetch(`https://endpoint/FileUpload/${body.nombre}`, {
method: 'POST',
body: data,
headers: {Authorization: `Bearer ${accessToken}`}
})
이게 도움이 됐으면 좋겠어요.
언급URL : https://stackoverflow.com/questions/30176570/using-django-rest-framework-how-can-i-upload-a-file-and-send-a-json-payload
'programing' 카테고리의 다른 글
모멘트를 사용하여 날짜 목록에서 최소 날짜 또는 최대 날짜를 얻는 방법? (0) | 2023.02.22 |
---|---|
AJAX 콜을 처리하는 웹 워커 - 최적화 오버킬? (0) | 2023.02.22 |
각도 JS각도 JS동봉하지 않은 검증동봉하지 않은 검증 (0) | 2023.02.22 |
AngularJS : $scope를 호출할 때 이미 진행 중인 오류 $digest를 방지합니다.$syslog() (0) | 2023.02.22 |
SQL Server에서의 지연 가능한 제약 조건 (0) | 2023.02.22 |