Django 剩余框架文件上传

我使用 Django Rest Framework 和 AngularJs 来上传一个文件,我的视图文件如下:

class ProductList(APIView):
authentication_classes = (authentication.TokenAuthentication,)
def get(self,request):
if request.user.is_authenticated():
userCompanyId = request.user.get_profile().companyId
products = Product.objects.filter(company = userCompanyId)
serializer = ProductSerializer(products,many=True)
return Response(serializer.data)


def post(self,request):
serializer = ProductSerializer(data=request.DATA, files=request.FILES)
if serializer.is_valid():
serializer.save()
return Response(data=request.DATA)

由于 post 方法的最后一行应该返回所有数据,我有几个问题:

  • 如何检查是否有任何在 request.FILES
  • 如何序列化文件字段?
  • 我应该如何使用解析器?
224443 次浏览

使用 FileUploadParser,这都在请求中。 使用 put 方法,你可以在文档中找到一个例子:)

class FileUploadView(views.APIView):
parser_classes = (FileUploadParser,)


def put(self, request, filename, format=None):
file_obj = request.FILES['file']
# do some stuff with uploaded file
return Response(status=204)

编者按:

  • 这个答案使用 pre_save,它在 Django REST 框架3.0中已经不存在了。
  • 在 Django REST 框架的一个足够新的版本中,缺省情况下应该可以使用 MultiPartParser,它允许上传文件而不需要特殊处理。有关示例,请参见 下面是答案

我正在使用相同的堆栈,并且也在寻找一个文件上传的示例,但是我的情况更简单,因为我使用的是 ModelViewSet 而不是 APIView。关键是 pre _ save 挂钩。最后我把它和角度文件上传模块一起使用,比如:

# Django
class ExperimentViewSet(ModelViewSet):
queryset = Experiment.objects.all()
serializer_class = ExperimentSerializer


def pre_save(self, obj):
obj.samplesheet = self.request.FILES.get('file')


class Experiment(Model):
notes = TextField(blank=True)
samplesheet = FileField(blank=True, default='')
user = ForeignKey(User, related_name='experiments')


class ExperimentSerializer(ModelSerializer):
class Meta:
model = Experiment
fields = ('id', 'notes', 'samplesheet', 'user')
// AngularJS
controller('UploadExperimentCtrl', function($scope, $upload) {
$scope.submit = function(files, exp) {
$upload.upload({
url: '/api/experiments/' + exp.id + '/',
method: 'PUT',
data: {user: exp.user.id},
file: files[0]
});
};
});

最后,我能够上传图片使用 Django。这是我的工作代码

视野,视野

class FileUploadView(APIView):
parser_classes = (FileUploadParser, )


def post(self, request, format='jpg'):
up_file = request.FILES['file']
destination = open('/Users/Username/' + up_file.name, 'wb+')
for chunk in up_file.chunks():
destination.write(chunk)
destination.close()  # File should be closed only after all chuns are added


# ...
# do some stuff with uploaded file
# ...
return Response(up_file.name, status.HTTP_201_CREATED)

Urls.py

urlpatterns = patterns('',
url(r'^imageUpload', views.FileUploadView.as_view())

请求上传

curl -X POST -S -H -u "admin:password" -F "file=@img.jpg;type=image/jpg" 127.0.0.1:8000/resourceurl/imageUpload

我用 ModelViewSet 和 ModelSerializer 解决了这个问题,希望对社区有所帮助。

我还喜欢在序列化程序本身而不是在视图中进行验证和 Object-> JSON (反之亦然)登录。

让我们通过例子来理解它。

比如,我想创建 FileUploader API。它将在数据库中存储 id、 file _ path、 file _ name、 size、 owner 等字段。见下面的样本模型:

class FileUploader(models.Model):
file = models.FileField()
name = models.CharField(max_length=100) #name is filename without extension
version = models.IntegerField(default=0)
upload_date = models.DateTimeField(auto_now=True, db_index=True)
owner = models.ForeignKey('auth.User', related_name='uploaded_files')
size = models.IntegerField(default=0)

现在,对于 API,我想要的是:

  1. 得到:

当触发 GET 端点时,我希望每个上传的文件都包含以上所有字段。

  1. 帖子:

但是对于用户创建/上传文件,为什么她要担心传递所有这些字段。她可以只上传文件,然后,我想,序列化程序可以从上传的文件中获得其余的字段。

搜索器: 问: 我创建了下面的序列化程序来实现我的目的,但是不确定这是否是实现它的正确方法。

class FileUploaderSerializer(serializers.ModelSerializer):
# overwrite = serializers.BooleanField()
class Meta:
model = FileUploader
fields = ('file','name','version','upload_date', 'size')
read_only_fields = ('name','version','owner','upload_date', 'size')


def validate(self, validated_data):
validated_data['owner'] = self.context['request'].user
validated_data['name'] = os.path.splitext(validated_data['file'].name)[0]
validated_data['size'] = validated_data['file'].size
#other validation logic
return validated_data


def create(self, validated_data):
return FileUploader.objects.create(**validated_data)

参考视图:

class FileUploaderViewSet(viewsets.ModelViewSet):
serializer_class = FileUploaderSerializer
parser_classes = (MultiPartParser, FormParser,)


# overriding default query set
queryset = LayerFile.objects.all()


def get_queryset(self, *args, **kwargs):
qs = super(FileUploaderViewSet, self).get_queryset(*args, **kwargs)
qs = qs.filter(owner=self.request.user)
return qs

在 django-rest-Framework 中,请求数据由 Parsers解析。
Http://www.django-rest-framework.org/api-guide/parsers/

默认情况下,django-rest-Framework 采用解析器类 JSONParser。它将把数据解析为 json。因此,不会用它来解析文件。
如果我们希望将文件与其他数据一起进行解析,我们应该使用下面的解析器类之一。

FormParser
MultiPartParser
FileUploadParser

在这上面花了一天的时间,我发现..。

对于需要上传文件和发送数据的人来说,没有直接的方法可以让它工作。在 json api 规范中有一个 未解决的问题用于此。我看到的一种可能性是使用 multipart/related,如图所示的 给你,但我认为在 drf 中实现它非常困难。

最后,我实现的是将请求作为 formdata发送。您可以将每个文件作为 文件发送,将所有其他数据作为文本发送。 现在,要将数据作为文本发送,您有两个选择。Case 1)您可以将每个数据作为键值对发送,case 2)您可以有一个名为 资料的键,并将整个 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')

Py-> 不需要特殊更改,因为可写的 ManyTomany Field 实现,这里没有显示我的序列化器,因为它太长了。

视野,视野

class PostsViewset(viewsets.ModelViewSet):
serializer_class = PostsSerializer
#parser_classes = (MultipartJsonParser, parsers.JSONParser) use this if you have simple key value pair as data with no nested serializers
#parser_classes = (parsers.MultipartParser, parsers.JSONParser) use this if you want to parse json in the key value pair data sent
queryset = Posts.objects.all()
lookup_field = 'id'

现在,如果您遵循第一个方法并且只以键值对的形式发送非 Json 数据,则不需要自定义解析器类。DRF 的 MultipartParser 将完成这项工作。但是对于第二种情况,或者如果您有嵌套的序列化器(如我所示) ,您将需要自定义解析器,如下所示。

公用事业公司

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 = {}


# for case1 with nested serializers
# parse each field with json
for key, value in result.data.items():
if type(value) != str:
data[key] = value
continue
if '{' in value or "[" in value:
try:
data[key] = json.loads(value)
except ValueError:
data[key] = value
else:
data[key] = value


# for case 2
# 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)

这个序列化程序基本上会解析值中的任何 json 内容。

两种情况下邮递员的请求示例: 情况1 < img src = “ https://i.stack.imgur.com/xgYod.png”alt = “ case 1”> < img src = “ https://i.stack.imgur.com/xgYod.png”alt = “ case 1”> ,

案例2case2

    from rest_framework import status
from rest_framework.response import Response
class FileUpload(APIView):
def put(request):
try:
file = request.FILES['filename']
#now upload to s3 bucket or your media file
except Exception as e:
print e
return Response(status,
status.HTTP_500_INTERNAL_SERVER_ERROR)
return Response(status, status.HTTP_200_OK)
def post(self,request):
serializer = ProductSerializer(data=request.DATA, files=request.FILES)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)

我想写另一个选项,我觉得更干净,更容易维护。我们将使用 defaultRouter 为我们的视图集添加 CRUD url,并且我们将添加一个固定的 url,指定同一视图集中的上传者视图。

**** views.py


from rest_framework import viewsets, serializers
from rest_framework.decorators import action, parser_classes
from rest_framework.parsers import JSONParser, MultiPartParser
from rest_framework.response import Response
from rest_framework_csv.parsers import CSVParser
from posts.models import Post
from posts.serializers import PostSerializer




class PostsViewSet(viewsets.ModelViewSet):


queryset = Post.objects.all()
serializer_class = PostSerializer
parser_classes = (JSONParser, MultiPartParser, CSVParser)




@action(detail=False, methods=['put'], name='Uploader View', parser_classes=[CSVParser],)
def uploader(self, request, filename, format=None):
# Parsed data will be returned within the request object by accessing 'data' attr
_data = request.data


return Response(status=204)

项目的主要 urls.py

**** urls.py


from rest_framework import routers
from posts.views import PostsViewSet




router = routers.DefaultRouter()
router.register(r'posts', PostsViewSet)


urlpatterns = [
url(r'^posts/uploader/(?P<filename>[^/]+)$', PostsViewSet.as_view({'put': 'uploader'}), name='posts_uploader')
url(r'^', include(router.urls), name='root-api'),
url('admin/', admin.site.urls),
]

。-自述。

当我们将@action 装饰符添加到类方法‘ uploader’时,奇迹发生了。通过指定“ method = [‘ PUT’]”参数,我们只允许 PUT 请求; 非常适合文件上传。

我还添加了参数“ parser _ classes”,以显示您可以选择解析内容的解析器。我从 rest _ Framework _ csv 包中添加了 CSVParser,以演示如果需要这个功能,我们如何只接受特定类型的文件,在我的例子中,我只接受“ Content-Type: text/csv”。 注意: 如果您要添加自定义解析器,您需要在 ViewSet 中的 parser _ classes 中指定它们,因为在访问上传方法解析器之前,请求将比较允许的 media _ type 和 main (class)解析器。

现在我们需要告诉 Django 如何使用这个方法,以及在哪里可以在 URL 中实现。这就是我们添加固定 URL 的时候(简单的目的)。这个 Url 将接受一个“ filename”参数,稍后将在方法中传递该参数。我们需要将这个方法“ uploader”传递给 PostsViewSet.as _ view 方法,在列表中指定 http 协议(‘ PUT’)。

当我们降落在下面的网址

 http://example.com/posts/uploader/

它将期待一个带有头部指定“ Content-Type”和 Content-Disposition: atting; filename = “ something. csv”的 PUT 请求。

curl -v -u user:pass http://example.com/posts/uploader/ --upload-file ./something.csv --header "Content-type:text/csv"

根据我的经验,您不需要对 file 字段做任何特定的操作,您只需告诉它使用 file 字段:

from rest_framework import routers, serializers, viewsets


class Photo(django.db.models.Model):
file = django.db.models.ImageField()


def __str__(self):
return self.file.name


class PhotoSerializer(serializers.ModelSerializer):
class Meta:
model = models.Photo
fields = ('id', 'file')   # <-- HERE


class PhotoViewSet(viewsets.ModelViewSet):
queryset = models.Photo.objects.all()
serializer_class = PhotoSerializer


router = routers.DefaultRouter()
router.register(r'photos', PhotoViewSet)


api_urlpatterns = ([
url('', include(router.urls)),
], 'api')
urlpatterns += [
url(r'^api/', include(api_urlpatterns)),
]

你就可以上传文件了:

curl -sS http://example.com/api/photos/ -F 'file=@/path/to/file'

为模型中的每个额外字段添加 -F field=value。不要忘记添加身份验证。

这是我应用的方法之一,希望它能有所帮助。

     class Model_File_update(APIView):
parser_classes = (MultiPartParser, FormParser)
permission_classes = [IsAuthenticated]  # it will check if the user is authenticated or not
authentication_classes = [JSONWebTokenAuthentication]  # it will authenticate the person by JSON web token


def put(self, request):
id = request.GET.get('id')
obj = Model.objects.get(id=id)
serializer = Model_Upload_Serializer(obj, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
else:
return Response(serializer.errors, status=400)

如果有人对最简单的 Django 剩余框架的 ModelViewset 示例感兴趣。

模特是,

class MyModel(models.Model):
name = models.CharField(db_column='name', max_length=200, blank=False, null=False, unique=True)
imageUrl = models.FileField(db_column='image_url', blank=True, null=True, upload_to='images/')


class Meta:
managed = True
db_table = 'MyModel'

序列化器,

class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = "__all__"

观点是,

class MyModelView(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer

邮递员测试,

enter image description here

您可以通过生成一个解析器类来解析特定字段,然后将这些字段直接提供给标准 DRF 序列化器,从而将@Nithin 的回答推广到 DRF 现有的序列化器系统:

from django.http import QueryDict
import json
from rest_framework import parsers




def gen_MultipartJsonParser(json_fields):
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
qdict = QueryDict('', mutable=True)
for json_field in json_fields:
json_data = result.data.get(json_field, None)
if not json_data:
continue
data = json.loads(json_data)
if type(data) == list:
for d in data:
qdict.update({json_field: d})
else:
qdict.update({json_field: data})


return parsers.DataAndFiles(qdict, result.files)


return MultipartJsonParser

这个用法是这样的:

class MyFileViewSet(ModelViewSet):
parser_classes = [gen_MultipartJsonParser(['tags', 'permissions'])]
#                                           ^^^^^^^^^^^^^^^^^^^
#                              Fields that need to be further JSON parsed
....

如果您正在使用 ModelViewSet,那么实际上您已经完成了!它为你处理一切事情!您只需将该字段放入 ModelSerializer 中,并在客户机中设置 content-type=multipart/form-data;

但是正如您所知道的,您不能以 json 格式发送文件。(当内容类型设置为客户端的 application/json 时)。除非使用 Base64格式。

所以你有两个选择:

  • ModelViewSetModelSerializer处理作业并使用 content-type=multipart/form-data;发送请求
  • ModelSerializer中的字段设置为 Base64ImageField (or) Base64FileField,并告诉您的客户端将文件编码为 Base64,然后将 content-type=application/json设置为

Models.py

from django.db import models


import uuid


class File(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
file = models.FileField(blank=False, null=False)
    

def __str__(self):
return self.file.name

序列化器 py

from rest_framework import serializers
from .models import File


class FileSerializer(serializers.ModelSerializer):
class Meta:
model = File
fields = "__all__"

视野,视野

from django.shortcuts import render
from rest_framework.parsers import FileUploadParser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status


from .serializers import FileSerializer




class FileUploadView(APIView):
permission_classes = []
parser_class = (FileUploadParser,)


def post(self, request, *args, **kwargs):


file_serializer = FileSerializer(data=request.data)


if file_serializer.is_valid():
file_serializer.save()
return Response(file_serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Urls.py

from apps.files import views as FileViews


urlpatterns = [
path('api/files', FileViews.FileUploadView.as_view()),
]

设置

# file uload parameters
MEDIA_URL =  '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

api/files发送一个发帖请求,并将您的文件附加到 form-data字段 file。该文件将被上传到 /media文件夹,并添加一个带有 id 和文件名的 db 记录。

有些解决方案已经过时了(请求.data 应该用于 Django 3.0 +)。其中一些不验证输入。另外,我希望有一个解决方案与大摇大摆的注释。因此,我建议使用以下代码:

from drf_yasg.utils import swagger_auto_schema
from rest_framework import serializers
from rest_framework.parsers import MultiPartParser
from rest_framework.response import Response
from rest_framework.views import APIView




class FileUploadAPI(APIView):
parser_classes = (MultiPartParser, )


class InputSerializer(serializers.Serializer):
image = serializers.ImageField()


@swagger_auto_schema(
request_body=InputSerializer
)
def put(self, request):
input_serializer = self.InputSerializer(data=request.data)
input_serializer.is_valid(raise_exception=True)


# process file
file = input_serializer.validated_data['image']


return Response(status=204)


from rest_framework import status, generics
from rest_framework.response import Response
from rest_framework import serializers
import logging
logger = logging.getLogger(__name__)`enter code here`
class ImageUploadSerializer(serializers.Serializer):
file = serializers.FileField()


class UploadImages(generics.GenericAPIView):
    

serializer_class = ImageUploadSerializer
permission_classes = [IsAuthenticated, ]


def post(self, request):


try:
data = self.serializer_class(data=request.data)
if data.is_valid() is False:
return Response({'error': ERROR_MESSAGES.get('400')}, status=status.HTTP_400_BAD_REQUEST)
is_file_upload_success, file_item = save_aws_article_image(data.validated_data.get('file'),
request.user, upload_type)


if is_file_upload_success:
logger.info('{0} file uploaded {1}'.format(file_item['file_obj'].path, datetime.now()))
return Response({'path': file_item['file_obj'].path, 'id': file_item['file_obj'].uuid,
'name': file_item['file_obj'].name},
status=status.HTTP_201_CREATED)
except Exception as e:
logger.error(e, exc_info=True)
return Response({"error": e}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

一个 DRF 视图文件上传示例,使用 React (axios)发送一个 AudioBlob:

class MyViewSet(viewsets.ModelViewSet):
parser_classes = (MultiPartParser, FormParser)
queryset = MyModel.objects.all().order_by('created_at')
serializer_class = MySerializer

序列化程序:

class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'

型号:

class MyModel(models.Model):
sentence = models.ForeignKey(Sentence, related_name="voice_sentence", on_delete=models.CASCADE)
voice_record = models.FileField(blank=True, default='')
created_at = models.DateTimeField(auto_now_add=True)

公理:

export const sendSpeechText = async (audioBlob: any) => {
const headers = {
'Content-Type': 'application/json',
'Content-Disposition': 'attachment; filename=audiofile.webm'
}


const audiofile = new File([audioBlob], "audiofile.webm", { type: "audio/webm" })


const formData = new FormData();
formData.append("sentence", '1');
formData.append("voice_record", audiofile);


return await axios.post(
SEND_SPEECH_URL,
formData,
{
crossDomain: true,
headers: headers
},
)
}

注意: formData 中的 voice _ record 在模型中应该是相同的

我使用这个视图来上传文件到 aws。在这里,load _ file 是一个 helper 函数,而总的来说,您可以使用这个视图来获得表单数据形式的文件上传。

class FileUploadView(GenericAPIView):


def post(self, request):
try:
file = request.data['file']
if file.content_type == 'image/png' or file.content_type == 'image/jpeg':
file_name = upload_file(file)
return Response({"name": file_name}, status=status.HTTP_202_ACCEPTED)
else:
raise UnsupportedMediaType(file.content_type)
except KeyError:
return Response("file missing.", status=status.HTTP_404_NOT_FOUND)

在 drf 中主要有3种上传文件的方法

假设您有带有 书名标志字段以及 TagSerializer 的 Tag 模型

class Tag(models.Model):
title = models.CharField(max_length=10, default='')
file = models.FileField(upload_to='tag/', blank=True, null=True, )




class TagSerializer(rest_serializers.ModelSerializer):
class Meta:
model = Tag
fields = '__all__'

你可以根据自己的情况选择其中一个。

1-使用序列化程序:

class UploadFile(APIView):
parser_classes = (MultiPartParser, FormParser)


def post(self, request):


serializer = TagSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()


return Response(serializer.data, status=status.HTTP_200_OK)

2-使用写入法:

def save_file(file: InMemoryUploadedFile, full_path):
with open(full_path, 'wb+') as f:
for chunk in file.chunks():
f.write(chunk)




class UploadFile(APIView):
parser_classes = (MultiPartParser, FormParser)


def post(self, request):
file: InMemoryUploadedFile = request.FILES['file']
# define file_save_path variable
full_path = file_save_path + file.name
save_file(file, full_path)
return Response(serializer.data, status=status.HTTP_200_OK)

3-使用 FileSystemStorage:

class UploadFile(APIView):
parser_classes = (MultiPartParser, FormParser)


def post(self, request):
file: InMemoryUploadedFile = request.FILES['file']
f = FileSystemStorage()
# this will save file in MEDIA_ROOT path
f.save(file.name, file)
return Response(serializer.data, status=status.HTTP_200_OK)

对于希望使用或更喜欢使用 基于函数的视图上传文件的用户。

这是创建模型 > 视图 > 序列化器 > URL 和使用 Postman 测试端点的完整指南。我已经在需要的地方将注释放入代码中。

# models.py
# Imports
from django.db import models
import os


def document_path_and_name(instance, filename):
'''  Change the filename to 'instance_id + document_name '''
ext = filename.split('.')[-1]
filename = "%s_%s.%s" % (instance.id, instance.document_name, ext)


''' if document_name is 'doucment one' in pdf and id is 1
then filname will be saved as = 1_document_one.pdf '''


return os.path.join('files/', filename)


class Document(models.Model):
# I'm using document_name and id to give the filename that would be save with
# this using document_path_and_name function.
# you can modify on your need.
document_name = models.CharField(max_length=100)
file = models.FileField(upload_to=document_path_and_name)


def __str__(self):
return self.document_name

在这里,我们不需要 Serializer 来验证文件上传,但是如果我们需要序列化响应,则需要一个 Serializer。因此,让我们在本例中使用一个简单的 ReadOnly 序列化程序。

# serializers.py
# imports
from rest_framework import serializers


class DocumentSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
document_name = serializers.CharField(read_only=True)
file = serializers.URLField(read_only=True)

现在在 api_view中,我们将使用 MultiPartParser装饰器通过 POST 请求上传文件。我们需要一个 document_name和这个函数的文件来正确地上传文件,因为我们已经设置了模型。

# views.py
# imports
from rest_framework.decorators import api_view, parser_classes
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser
from .models import Document
from .serializers import DocumentSerializer


@api_view(['POST'])
@parser_classes([MultiPartParser])
def upload_document(request, filename, format=None):
"""
A view that can accept POST requests with .media_type: multipart/form-data content.
"""
file = request.FILES['file']
doc = Document.objects.create(document_name=filename, file=file)
# Do any thing else here
serializer = DocumentSerializer(doc, many=False)
return Response(serializer.data)

因此,我们将在 URL 参数中传递 document_name,我们可以称它为任何东西,但是我将它定义为 filename。 和我们的 API 端点或 Url 将类似;

# imports
from django.urls import path
from .views import upload_document


urlpatterns = [
path('upload_document/<str:filename>/', upload_document),
]

因此,要通过 Postman 测试这一点,请访问您的有效 API 端点,如下所示 enter image description here

我正在传递 document _ name 的文件名,您可以传递任何内容。您可能会注意到,在下面的截图中,实际的文件名是其他 PDF 格式的。这将在 id _ document _ name 的 document_path_and_name函数的帮助下被替换。这里的保存文件名是 1_filename.pdf

enter image description here

现在只要发出一个请求,您的文件将被上传到您的直接文件存储路径。您将从 DocumentSerializer 获得 JSON 响应。 负责文件上传的主要工作是 MultiPartParser装饰器。必须访问 医生了解更多细节。

如果您正在使用 ViewSet,您可以添加一个自定义操作来处理文件上传:

from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.parsers import FileUploadParser
from rest_framework import viewsets
...


class SomeViewSet(viewsets.ModelViewSet):
serializer_class = ...
permission_classes = [...]
queryset = ...


@action(methods=['put'], detail=True, parser_classes=[FileUploadParser])
def upload_file(self, request, pk=None):
obj = self.get_object()
obj.file = request.data['file']
obj.save()
return Response(status=204)

这将所有内容都保存在 ViewSet 中,您将得到一个类似于 api/item/32/upload_file/的端点。

与 multipart 等其他选项相比,使用 FileUploadParser的原因是,如果您从本地应用程序上传,而不想依赖于多部分编码器。

处理 单个上传文件或单个请求中的多个文件的最佳直接方式 是这样的

@api_view(['POST'])
def file_list(request):  # use APIview or function based view or any view u want
# for single file
file = request.FILES["file"]
print(file)
# Do what ever you want with it


# for multiple file
files = request.FILES.getlist('file')
for file in files:
print(file)
# Do what ever you want with it