我正在使用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
?
如何序列化文件字段?
我该如何使用解析器?
小智.. 72
我正在使用相同的堆栈,并且还在寻找文件上传的示例,但我的情况更简单,因为我使用ModelViewSet而不是APIView.密钥竟然是pre_save钩子.我最终将它与angular-file-upload模块一起使用,如下所示:
# 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] }); }; });
在drf 3.x中不推荐使用pre_save (10认同)
pleasedontbe.. 52
使用FileUploadParser,它都在请求中.使用put方法代替,你会在docs中找到一个例子:)
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)
@pleasedontbelong为什么在这里使用PUT方法而不是POST? (6认同)
嗨@pleasedontbelong,如果它正在创建一个新记录,它会改为POST吗?它还能用于FileUploadParser吗? (5认同)
对于第二个@ x-yuri,DRF抱怨当我使用FileUploadParser时Content-Disposition标头为空。MultiPartParser更为简单,因为它仅假定文件名是“表单”字段中的给定文件名。 (2认同)
Vipul J.. 29
最后,我可以使用Django上传图像.这是我的工作代码
views.py
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() # ... # 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请求上传
curl -X POST -S -H -u "admin:password" -F "file=@img.jpg;type=image/jpg" 127.0.0.1:8000/resourceurl/imageUpload
为什么destination.close()放在for循环的内部? (14认同)
似乎最好使用`with open('/ Users/Username /'+ up_file.name,'wb +')作为目标:`并完全删除关闭 (9认同)
Nithin.. 8
在此上花了1天后,我发现...
对于需要上载文件并发送一些数据的人,没有直接的前进方式可以使它工作。json api规范中有一个未解决的问题。我看到的一种可能性是使用此处multipart/related
所示的方法,但是我认为很难在drf中实现它。
最后,我实现的是将请求发送为formdata
。您将每个文件作为文件发送,所有其他数据作为文本发送。现在,以文本形式发送数据有两种选择。情况1)您可以将每个数据作为键值对发送,或者情况2)您可以有一个名为data的键,并将整个json作为值字符串发送。
如果您具有简单的字段,则第一种方法开箱即用,但如果嵌套了序列化,则将是一个问题。多部分解析器将无法解析嵌套字段。
下面我提供两种情况的实现
型号
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) 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将完成这项工作。但是对于第二种情况,或者如果您有嵌套的序列化器(如我所示),则需要自定义解析器,如下所示。
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 = {} # 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
情况二