Tải lên tệp khung công tác phần còn lại Django


98

Tôi đang sử dụng Django Rest Framework và AngularJs để tải tệp lên. Tệp xem của tôi trông như thế này:

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)

Vì dòng cuối cùng của phương thức đăng sẽ trả về tất cả dữ liệu, tôi có một số câu hỏi:

  • làm thế nào để kiểm tra xem có bất cứ điều gì trong request.FILES?
  • làm thế nào để tuần tự hóa trường tệp?
  • tôi nên sử dụng trình phân tích cú pháp như thế nào?

8
CHỈ CẦN LƯU Ý CHO CÁC CHẾ ĐỘ: Django đã nâng cấp rất nhiều kể từ năm 2013. Vì vậy, nếu ai đó đăng cùng câu hỏi bây giờ. VUI LÒNG đừng bắn chúng xuống ^ _ ^.
Jessi

Làm thế nào về Base64?
Hojat Modaresi

Câu trả lời:


67

Sử dụng FileUploadParser , tất cả đều nằm trong yêu cầu. Thay vào đó, hãy sử dụng phương pháp put, bạn sẽ tìm thấy một ví dụ trong tài liệu :)

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)

12
@pleasedontbelong tại sao phương thức PUT lại được sử dụng ở đây thay vì POST?
Md. Tanvir Raihan

8
xin chào @pleasedontbelong, nếu nó đang tạo một bản ghi mới, nó có phải là POST không? và nó sẽ vẫn hoạt động với FileUploadParser chứ?
nuttynibbles,

1
@pleasedontbelong RTan đặt một câu hỏi khá hay. Đọc RFC-2616 cung cấp một sự tinh tế mà tôi không biết cho đến bây giờ. "Sự khác biệt cơ bản giữa yêu cầu POST và PUT được phản ánh trong ý nghĩa khác nhau của Request-URI. URI trong yêu cầu POST xác định tài nguyên sẽ xử lý thực thể kèm theo. Tài nguyên đó có thể là quy trình chấp nhận dữ liệu, một cổng tới một số giao thức khác hoặc một thực thể riêng biệt chấp nhận chú thích. Ngược lại, URI trong một yêu cầu PUT xác định thực thể kèm theo yêu cầu "
dudeman

2
Tại sao lại sử dụng FileUploadParser? "FileUploadParser được sử dụng với các máy khách gốc có thể tải tệp lên dưới dạng yêu cầu dữ liệu thô. Để tải lên dựa trên web hoặc cho các máy khách gốc có hỗ trợ tải lên nhiều phần, thay vào đó bạn nên sử dụng trình phân tích cú pháp MultiPartParser." Nhìn chung có vẻ không phải là một lựa chọn tốt. Hơn nữa, tôi không thấy việc tải lên tệp cần bất kỳ xử lý cụ thể nào .
x-yuri

3
Đến @ x-yuri thứ hai, DRF phàn nàn về việc tiêu đề Nội dung-Bố trí trống khi tôi sử dụng FileUploadParser. MultiPartParser đơn giản hơn nhiều, vì nó chỉ giả sử tên tệp là tên tệp đã cho trong các trường Biểu mẫu.
David Zwart,

74

Tôi đang sử dụng cùng một ngăn xếp và cũng đang tìm kiếm một ví dụ về tải lên tệp, nhưng trường hợp của tôi đơn giản hơn vì tôi sử dụng ModelViewSet thay vì APIView. Chìa khóa hóa ra là hook pre_save. Tôi đã kết thúc việc sử dụng nó cùng với mô-đun angle-file-upload như sau:

# 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]
        });
    };
});

11
pre_save không được dùng nữa trong drf 3.x
Guy S

Theo kinh nghiệm của tôi, không cần xử lý đặc biệt nào cho các trường tệp.
x-yuri

Các phương thức @ Guy-S, performance_create, performance_update, performance_destroy thay thế cho các phương thức kiểu cũ 2.x pre_save, post_save, pre_delete và post_delete, không còn khả dụng: django-rest-framework.org/api-guide/generic-views / # phương pháp
Rufat

37

Cuối cùng, tôi có thể tải lên hình ảnh bằng Django. Đây là mã làm việc của tôi

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()  # 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())

yêu cầu cuộn tròn để tải lên

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

14
tại sao đích.close () được đặt bên trong vòng lặp for?
makerj

12
Có vẻ như nó sẽ là tốt hơn để sử dụng with open('/Users/Username/' + up_file.name, 'wb+') as destination:và loại bỏ kết thúc hoàn toàn
Chuck Wilbur

Nó đơn giản hơn để sử dụng ModelViewSet. Ngoài ra, rất có thể họ đã triển khai nó tốt hơn.
x-yuri

Tôi đã dựa vào trình trả lời này cả ngày ... cho đến khi tôi phát hiện ra rằng khi bạn muốn tải lên nhiều tệp, điều đó không FileUploadParsercần thiết, nhưng MultiPartParser!
Olivier Pons

13

Sau khi dành 1 ngày cho việc này, tôi nhận ra rằng ...

Đối với những người cần tải lên một tệp và gửi một số dữ liệu, không có cách nào dễ dàng để bạn có thể làm cho nó hoạt động. Có một vấn đề mở trong thông số kỹ thuật json api cho điều này. Một khả năng tôi đã thấy là sử dụng multipart/relatednhư được hiển thị ở đây , nhưng tôi nghĩ rất khó để thực hiện nó trong drf.

Cuối cùng những gì tôi đã thực hiện là gửi yêu cầu dưới dạng formdata. Bạn sẽ gửi từng tệp dưới dạng tệp và tất cả dữ liệu khác dưới dạng văn bản. Bây giờ để gửi dữ liệu dưới dạng văn bản, bạn có hai lựa chọn. trường hợp 1) bạn có thể gửi từng dữ liệu dưới dạng cặp giá trị khóa hoặc trường hợp 2) bạn có thể có một khóa duy nhất được gọi là dữ liệu và gửi toàn bộ json dưới dạng chuỗi trong giá trị.

Phương pháp đầu tiên sẽ hoạt động tốt nếu bạn có các trường đơn giản, nhưng sẽ là một vấn đề nếu bạn có các chuỗi tuần tự lồng nhau. Trình phân tích cú pháp nhiều phần sẽ không thể phân tích cú pháp các trường lồng nhau.

Dưới đây tôi cung cấp cách triển khai cho cả hai trường hợp

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 -> không cần thay đổi đặc biệt nào, không hiển thị trình tuần tự của tôi ở đây vì nó quá dài do áp lực ManyToMany Field có thể ghi được.

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'

Bây giờ, nếu bạn đang làm theo phương pháp đầu tiên và chỉ gửi dữ liệu không phải Json dưới dạng các cặp giá trị khóa, thì bạn không cần một lớp phân tích cú pháp tùy chỉnh. DRF'd MultipartParser sẽ thực hiện công việc. Nhưng đối với trường hợp thứ hai hoặc nếu bạn có các bộ tuần tự lồng nhau (như tôi đã trình bày), bạn sẽ cần trình phân tích cú pháp tùy chỉnh như được hiển thị bên dưới.

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)

Bộ tuần tự này về cơ bản sẽ phân tích cú pháp bất kỳ nội dung json nào trong các giá trị.

Ví dụ yêu cầu trong post man cho cả hai trường hợp: trường hợp 1 trường hợp 1 ,

Trường hợp 2 case2


Tôi muốn tránh trường hợp 2. Việc tạo một bản ghi cơ sở dữ liệu cho mỗi yêu cầu sẽ ổn trong hầu hết thời gian.
x-yuri

rất hữu ích cảm ơn bạn rất nhiều. Nhưng tôi không hiểu, tại sao bạn lại chuyển đổi dữ liệu dict thành QueryDict trong trình phân tích cú pháp? Trong trường hợp của tôi ở Django, dữ liệu từ điển bình thường hoạt động hoàn hảo mà không cần chuyển đổi.
Metehan Gülaç

Tôi đã thử một kịch bản khác bằng cách sử dụng câu trả lời bạn đã đề cập và nó hoạt động thành công. bạn có thể nhìn vào câu trả lời của tôi .
Metehan Gülaç

7

Tôi đã giải quyết vấn đề này với ModelViewSet và ModelSerializer. Hy vọng điều này sẽ giúp ích cho cộng đồng.

Tôi cũng yêu cầu xác thực và đăng nhập Object-> JSON (và ngược lại) trong chính bộ tuần tự hóa chứ không phải trong các khung nhìn.

Hãy hiểu nó bằng ví dụ.

Giả sử, tôi muốn tạo API FileUploader. Nơi nó sẽ lưu trữ các trường như id, file_path, file_name, size, owner, v.v. trong cơ sở dữ liệu. Xem mô hình mẫu bên dưới:

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)

Bây giờ, đối với API, đây là những gì tôi muốn:

  1. ĐƯỢC:

Khi tôi kích hoạt điểm cuối GET, tôi muốn tất cả các trường trên cho mọi tệp được tải lên.

  1. BÀI ĐĂNG:

Nhưng để người dùng tạo / tải lên tệp, tại sao họ phải lo lắng về việc vượt qua tất cả các trường này. Cô ấy chỉ có thể tải tệp lên và sau đó, tôi cho rằng bộ tuần tự hóa có thể lấy phần còn lại của các trường từ FILE đã tải lên.

Searilizer: Câu hỏi: Tôi đã tạo bộ tuần tự dưới đây để phục vụ mục đích của mình. Nhưng không chắc liệu nó có đúng cách để thực hiện nó hay không.

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)

Chế độ xem để tham khảo:

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

FileUploaderSerializer.validatePhương thức chứa logic xác thực nào ?
x-yuri

7

Theo kinh nghiệm của tôi, bạn không cần phải làm bất cứ điều gì cụ thể về trường tệp, bạn chỉ cần yêu cầu nó sử dụng trường tệp:

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)),
]

và bạn đã sẵn sàng tải tệp lên:

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

Thêm -F field=valuecho mỗi trường bổ sung mà mô hình của bạn có. Và đừng quên thêm xác thực.


4

Nếu ai quan tâm đến ví dụ đơn giản nhất với ModelViewset cho Django Rest Framework.

Mô hình là,

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'

Bộ nối tiếp,

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

Và View là,

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

Kiểm tra trong Postman,

nhập mô tả hình ảnh ở đây


Và làm thế nào chúng tôi có thể gửi yêu cầu bằng ajax. ImageUrl thực sự là gì?
Eduard Grigoryev

imageUrl là tệp trong yêu cầu.
sadat

0

Trong django-rest-framework, dữ liệu yêu cầu được phân tích cú pháp bởi Parsers.
http://www.django-rest-framework.org/api-guide/parsers/

Theo mặc định, django-rest-framework có lớp phân tích cú pháp JSONParser. Nó sẽ phân tích cú pháp dữ liệu thành json. vì vậy, các tệp sẽ không được phân tích cú pháp với nó.
Nếu chúng ta muốn tệp được phân tích cú pháp cùng với dữ liệu khác, chúng ta nên sử dụng một trong các lớp phân tích cú pháp bên dưới.

FormParser
MultiPartParser
FileUploadParser

Trên phiên bản hiện tại của DRF 3.8.2, nó sẽ phân tích theo mặc định application/json, application/x-www-form-urlencodedmultipart/form-data.
liquidki

0
    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)

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

0

Tôi muốn viết một tùy chọn khác mà tôi cảm thấy sạch hơn và dễ bảo trì hơn. Chúng tôi sẽ sử dụng defaultRouter để thêm các url CRUD cho tập xem của mình và chúng tôi sẽ thêm một url cố định khác chỉ định chế độ xem của người tải lên trong cùng một tập xem.

**** 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 chính của dự án

**** 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),
]

.- ĐỌC HIỂU.

Điều kỳ diệu xảy ra khi chúng ta thêm @action decorator vào 'trình tải lên' của phương thức lớp của chúng ta. Bằng cách chỉ định đối số "method = ['put']", chúng tôi chỉ cho phép các yêu cầu PUT; hoàn hảo để tải lên tệp.

Tôi cũng đã thêm đối số "parser_classes" để cho thấy bạn có thể chọn trình phân tích cú pháp sẽ phân tích cú pháp nội dung của bạn. Tôi đã thêm CSVParser từ gói rest_framework_csv, để chứng minh cách chúng tôi có thể chỉ chấp nhận một số loại tệp nhất định nếu chức năng này là bắt buộc, trong trường hợp của tôi, tôi chỉ chấp nhận "Loại nội dung: text / csv". Lưu ý: Nếu bạn đang thêm Trình phân tích cú pháp tùy chỉnh, bạn sẽ cần chỉ định chúng trong parsers_classes trong ViewSet do yêu cầu sẽ so sánh media_type được phép với trình phân tích cú pháp chính (lớp) trước khi truy cập trình phân tích cú pháp phương thức tải lên.

Bây giờ chúng ta cần cho Django biết cách đi đến phương thức này và nơi có thể được triển khai trong các url của chúng ta. Đó là khi chúng ta thêm url cố định (Mục đích đơn giản). Url này sẽ nhận đối số "tên tệp" sẽ được chuyển vào phương thức sau này. Chúng ta cần chuyển phương thức này "người tải lên", chỉ định giao thức http ('PUT') trong danh sách cho phương thức PostsViewSet.as_view.

Khi chúng ta truy cập vào url sau

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

nó sẽ mong đợi một yêu cầu PUT với các tiêu đề chỉ định "Loại nội dung" và Bố trí nội dung: tệp đính kèm; filename = "something.csv".

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

Vì vậy, bạn đề xuất tải lên một tệp, sau đó đính kèm nó vào một số bản ghi db. Điều gì sẽ xảy ra nếu đính kèm không bao giờ xảy ra vì một số lý do? Tại sao không làm điều đó trong một yêu cầu? parser_classeskhông có để giới hạn tệp có thể được tải lên. Nó cho phép bạn quyết định định dạng nào có thể được sử dụng để đưa ra yêu cầu. Suy nghĩ thứ hai, cách bạn xử lý tải lên ... có vẻ như bạn đang đưa dữ liệu từ CSV vào cơ sở dữ liệu. Không phải những gì OP yêu cầu.
x-yuri

@ x-yuri bằng cách nói "CSV là một tệp" và câu hỏi là; Làm thế nào để kiểm tra xem có dữ liệu trong yêu cầu không? Bằng cách sử dụng phương pháp này, bạn sẽ tìm thấy dữ liệu trong request.data. _data = request.data do PUT đang được sử dụng. Giống như bạn đã nói, parser_classes ở đó để quyết định những định dạng CÓ THỂ được sử dụng để yêu cầu bằng cách sử dụng bất kỳ định dạng nào khác mà bạn KHÔNG muốn, sau đó sẽ bị loại trừ khi thêm một lớp bảo mật bổ sung. Bạn phải làm gì với dữ liệu của mình là tùy thuộc vào bạn. Sử dụng "Thử ngoại trừ", bạn có thể kiểm tra xem "đính kèm không bao giờ xảy ra" hay không vì không cần nó, đó không phải là những gì mã làm. Những được thực hiện trong 1 yêu cầu
Wolfgang Leon

0

Đây là một trong những cách tiếp cận mà tôi đã áp dụng hy vọng nó sẽ hữu ích.

     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)

0

Bạn có thể khái quát câu trả lời của @ Nithin để làm việc trực tiếp với hệ thống bộ tuần tự hiện có của DRF bằng cách tạo lớp phân tích cú pháp để phân tích cú pháp các trường cụ thể, sau đó được đưa trực tiếp vào bộ tuần tự DRF tiêu chuẩn:

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

Điều này được sử dụng như:

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

0

Nếu bạn đang sử dụng ModelViewSet, thực sự là bạn đã hoàn tất! Nó xử lý mọi thứ cho bạn! Bạn chỉ cần đưa trường vào ModelSerializer và đặt content-type=multipart/form-data;trong ứng dụng khách của bạn.

NHƯNG như bạn biết bạn không thể gửi tệp ở định dạng json. (khi loại nội dung được đặt thành ứng dụng / json trong ứng dụng khách của bạn). Trừ khi bạn sử dụng định dạng Base64.

Vì vậy, bạn có hai lựa chọn:

  • để ModelViewSetModelSerializerxử lý công việc và gửi yêu cầu bằngcontent-type=multipart/form-data;
  • đặt trường ModelSerializerthành Base64ImageField (or) Base64FileFieldvà yêu cầu khách hàng của bạn mã hóa tệp Base64và đặtcontent-type=application/json

0

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

serializers.py

from rest_framework import serializers
from .models import File

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

views.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)

urls.py

from apps.files import views as FileViews

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

settings.py

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

Gửi yêu cầu đăng bài api/filesvới tệp của bạn được đính kèm với một form-datatrường file. Tệp sẽ được tải lên /mediathư mục và bản ghi db sẽ được thêm id và tên tệp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.