Paw*_*wan 76 python django angularjs django-rest-framework
我正在使用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)
Run Code Online (Sandbox Code Playgroud)
由于post方法的最后一行应该返回所有数据,我有几个问题:
request.FILES
?ybe*_*ana 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]
});
};
});
Run Code Online (Sandbox Code Playgroud)
ple*_*ong 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)
Run Code Online (Sandbox Code Playgroud)
Vip*_*l 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)
Run Code Online (Sandbox Code Playgroud)
urls.py
urlpatterns = patterns('',
url(r'^imageUpload', views.FileUploadView.as_view())
Run Code Online (Sandbox Code Playgroud)
curl请求上传
curl -X POST -S -H -u "admin:password" -F "file=@img.jpg;type=image/jpg" 127.0.0.1:8000/resourceurl/imageUpload
Run Code Online (Sandbox Code Playgroud)
如果有人对 Django Rest Framework 的 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'
Run Code Online (Sandbox Code Playgroud)
序列化器,
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = "__all__"
Run Code Online (Sandbox Code Playgroud)
视图是,
class MyModelView(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
Run Code Online (Sandbox Code Playgroud)
在邮递员中测试,
在此上花了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')
Run Code Online (Sandbox Code Playgroud)
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'
Run Code Online (Sandbox Code Playgroud)
现在,如果您遵循第一种方法,并且仅将非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)
Run Code Online (Sandbox Code Playgroud)
该序列化程序将基本上解析值中的所有json内容。
模型.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
Run Code Online (Sandbox Code Playgroud)
序列化程序.py
from rest_framework import serializers
from .models import File
class FileSerializer(serializers.ModelSerializer):
class Meta:
model = File
fields = "__all__"
Run Code Online (Sandbox Code Playgroud)
视图.py
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)
Run Code Online (Sandbox Code Playgroud)
网址.py
from apps.files import views as FileViews
urlpatterns = [
path('api/files', FileViews.FileUploadView.as_view()),
]
Run Code Online (Sandbox Code Playgroud)
设置.py
# file uload parameters
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Run Code Online (Sandbox Code Playgroud)
将api/files
您的文件附加到一个form-data
字段发送一个 post 请求file
。该文件将上传到/media
文件夹,并添加一个带有 id 和文件名的 db 记录。
我用 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)
Run Code Online (Sandbox Code Playgroud)
现在,对于 API,这就是我想要的:
当我触发 GET 端点时,我希望每个上传的文件都包含上述所有字段。
但是对于用户创建/上传文件,她为什么要担心传递所有这些字段。她可以上传文件,然后,我想,序列化程序可以从上传的文件中获取其余字段。
Searilizer: 问题:我创建了下面的序列化程序来达到我的目的。但不确定它是否是实现它的正确方法。
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)
Run Code Online (Sandbox Code Playgroud)
供参考的视图集:
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
Run Code Online (Sandbox Code Playgroud)
根据我的经验,您不需要对文件字段做任何特别的事情,只需告诉它使用文件字段即可:
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)),
]
Run Code Online (Sandbox Code Playgroud)
您就可以上传文件了:
curl -sS http://example.com/api/photos/ -F 'file=@/path/to/file'
Run Code Online (Sandbox Code Playgroud)
-F field=value
为模型具有的每个其他字段添加。并且不要忘记添加身份验证。
I'd like to write another option that I feel is cleaner and easier to maintain. We'll be using the defaultRouter to add CRUD urls for our viewset and we'll add one more fixed url specifying the uploader view within the same viewset.
**** 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)
Run Code Online (Sandbox Code Playgroud)
Project's main 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),
]
Run Code Online (Sandbox Code Playgroud)
.- README.
The magic happens when we add @action decorator to our class method 'uploader'. By specifying "methods=['put']" argument, we are only allowing PUT requests; perfect for file uploading.
I also added the argument "parser_classes" to show you can select the parser that will parse your content. I added CSVParser from the rest_framework_csv package, to demonstrate how we can accept only certain type of files if this functionality is required, in my case I'm only accepting "Content-Type: text/csv". Note: If you're adding custom Parsers, you'll need to specify them in parsers_classes in the ViewSet due the request will compare the allowed media_type with main (class) parsers before accessing the uploader method parsers.
Now we need to tell Django how to go to this method and where can be implemented in our urls. That's when we add the fixed url (Simple purposes). This Url will take a "filename" argument that will be passed in the method later on. We need to pass this method "uploader", specifying the http protocol ('PUT') in a list to the PostsViewSet.as_view method.
When we land in the following url
http://example.com/posts/uploader/
Run Code Online (Sandbox Code Playgroud)
it will expect a PUT request with headers specifying "Content-Type" and Content-Disposition: attachment; filename="something.csv".
curl -v -u user:pass http://example.com/posts/uploader/ --upload-file ./something.csv --header "Content-type:text/csv"
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
92303 次 |
最近记录: |