Pengunggahan File Kerangka Kerja Django Rest

100

Saya menggunakan Django Rest Framework dan AngularJs untuk mengunggah file. File tampilan saya terlihat seperti ini:

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)

Karena baris terakhir metode posting harus mengembalikan semua data, saya punya beberapa pertanyaan:

  • bagaimana cara memeriksa apakah ada sesuatu request.FILES?
  • bagaimana cara membuat serial file field?
  • bagaimana cara menggunakan parser?
Pawan
sumber
13
HANYA CATATAN UNTUK MODS: Django telah meningkatkan secara signifikan sejak 2013. Jadi jika orang lain memposting pertanyaan yang sama sekarang. TOLONG jangan tembak mereka ^ _ ^.
Jessi
Bagaimana dengan Base64?
Hojat Modaresi

Jawaban:

67

Gunakan FileUploadParser , semuanya ada dalam permintaan. Gunakan metode put sebagai gantinya, Anda akan menemukan contoh di dokumen :)

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)
senang tidak menjadi milik
sumber
12
@pleasedontbelong mengapa metode PUT telah digunakan di sini daripada POST?
Md. Tanvir Raihan
8
hai @pleasedontbelong, jika itu membuat rekor baru, apakah itu akan menjadi POST? dan apakah itu masih akan berfungsi dengan FileUploadParser?
chrizonline
1
@pleasedontbelong RTan mengajukan pertanyaan yang cukup bagus. Membaca RFC-2616 memberikan kehalusan yang tidak saya sadari sampai sekarang. "Perbedaan mendasar antara permintaan POST dan PUT tercermin dalam arti berbeda dari Request-URI. URI dalam permintaan POST mengidentifikasi sumber daya yang akan menangani entitas tertutup. Sumber daya tersebut mungkin merupakan proses penerimaan data, gateway ke beberapa protokol lain, atau entitas terpisah yang menerima anotasi. Sebaliknya, URI dalam permintaan PUT mengidentifikasi entitas yang disertakan dengan permintaan tersebut "
dudeman
3
Mengapa FileUploadParser? "FileUploadParser ditujukan untuk digunakan dengan klien asli yang dapat mengupload file sebagai permintaan data mentah. Untuk upload berbasis web, atau untuk klien asli dengan dukungan upload multi bagian, Anda harus menggunakan pengurai MultiPartParser." Sepertinya bukan pilihan yang baik secara umum. Terlebih lagi, saya tidak melihat unggahan file membutuhkan perlakuan khusus .
x-yuri
3
Untuk @ x-yuri kedua, DRF mengeluh tentang header Content-Disposition yang kosong saat saya menggunakan FileUploadParser. MultiPartParser jauh lebih sederhana, karena ini hanya mengasumsikan nama file menjadi nama file yang diberikan di kolom Formulir.
David Zwart
74

Saya menggunakan tumpukan yang sama dan juga mencari contoh unggahan file, tetapi kasus saya lebih sederhana karena saya menggunakan ModelViewSet, bukan APIView. Kuncinya ternyata adalah hook pre_save. Saya akhirnya menggunakannya bersama dengan modul angular-file-upload seperti:

# 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]
        });
    };
});
ybendana
sumber
11
pre_save tidak digunakan lagi di drf 3.x
Guy S
Dari pengalaman saya, tidak diperlukan perlakuan khusus untuk bidang file.
x-yuri
@ Guy-S, perform_create, perform_update, metode perform_destroy menggantikan metode gaya lama versi 2.x pre_save, post_save, pre_delete dan post_delete, yang tidak lagi tersedia: django-rest-framework.org/api-guide/generic-views / # metode
Rufat
38

Akhirnya saya bisa mengunggah gambar menggunakan Django. Ini kode kerja saya

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

curl permintaan untuk mengunggah

curl -X POST -S -H -u "admin:password" -F "[email protected];type=image/jpg" 127.0.0.1:8000/resourceurl/imageUpload
Vipul J
sumber
14
mengapa destination.close () ditempatkan di dalam for loop?
makerj
12
Tampaknya akan lebih baik menggunakan with open('/Users/Username/' + up_file.name, 'wb+') as destination:dan melepas tutup seluruhnya
Chuck Wilbur
Lebih mudah digunakan ModelViewSet. Juga, mereka kemungkinan besar menerapkannya dengan lebih baik.
x-yuri
Saya telah mengandalkan jawaban ini sepanjang hari ... sampai saya menemukan bahwa ketika Anda ingin mengunggah banyak file, itu tidak FileUploadParserdiperlukan, tetapi MultiPartParser!
Olivier Pons
13

Setelah menghabiskan 1 hari untuk ini, saya menemukan bahwa ...

Untuk seseorang yang perlu mengupload file dan mengirim beberapa data, tidak ada cara langsung untuk membuatnya bekerja. Ada masalah terbuka dalam spesifikasi json api untuk ini. Salah satu kemungkinan yang saya lihat adalah menggunakan multipart/relatedseperti yang ditunjukkan di sini , tetapi saya rasa sangat sulit untuk menerapkannya di drf.

Akhirnya apa yang telah saya terapkan adalah mengirim permintaan sebagai formdata. Anda akan mengirim setiap file sebagai file dan semua data lainnya sebagai teks. Sekarang untuk mengirim data sebagai teks Anda memiliki dua pilihan. case 1) Anda dapat mengirim setiap data sebagai key value pair atau case 2) Anda dapat memiliki satu kunci yang disebut data dan mengirim seluruh json sebagai nilai string.

Metode pertama akan bekerja di luar kotak jika Anda memiliki bidang sederhana, tetapi akan menjadi masalah jika Anda memiliki serialisasi bersarang. Pengurai multibagian tidak dapat mengurai bidang bertingkat.

Di bawah ini saya memberikan implementasi untuk kedua kasus tersebut

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 -> tidak diperlukan perubahan khusus, tidak menampilkan serializer saya di sini karena terlalu panjang karena implimentasi ManyToMany Field yang dapat ditulis.

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'

Sekarang, jika Anda mengikuti metode pertama dan hanya mengirim data non-Json sebagai key value pair, Anda tidak memerlukan class parser kustom. MultipartParser DRF akan melakukan pekerjaan itu. Tetapi untuk kasus kedua atau jika Anda memiliki serializers bersarang (seperti yang telah saya tunjukkan) Anda akan memerlukan parser khusus seperti yang ditunjukkan di bawah ini.

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)

Serializer ini pada dasarnya akan mengurai konten json apa pun dalam nilai.

Contoh request di post man untuk kedua kasus: case 1 kasus 1,

Kasus 2 kasus2

Nithin
sumber
Saya lebih suka menghindari kasus 2. Membuat satu record database per permintaan seharusnya baik-baik saja di sebagian besar waktu.
x-yuri
sangat membantu terima kasih banyak. Tetapi saya tidak mengerti, mengapa Anda mengonversi data dict ke QueryDict di parser? Dalam kasus saya di Django, data kamus normal bekerja sempurna tanpa mengubah.
Metehan Gülaç
Saya mencoba skenario yang berbeda menggunakan jawaban yang Anda sebutkan dan berhasil. Anda bisa melihat jawaban saya .
Metehan Gülaç
jika ini berhasil stackoverflow.com/questions/64547729/… harus bekerja juga., tetapi tidak.
sadat
9

Dari pengalaman saya, Anda tidak perlu melakukan sesuatu yang khusus tentang bidang file, Anda cukup memberi tahu untuk menggunakan bidang 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)),
]

dan Anda siap mengunggah file:

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

Tambahkan -F field=valueuntuk setiap bidang ekstra yang dimiliki model Anda. Dan jangan lupa untuk menambahkan otentikasi.

x-yuri
sumber
7

Saya memecahkan masalah ini dengan ModelViewSet dan ModelSerializer. Semoga ini bisa membantu masyarakat.

Saya juga lebih suka memiliki validasi dan Object-> JSON (dan sebaliknya) login di serializer itu sendiri daripada di views.

Mari kita pahami dengan contoh.

Katakanlah, saya ingin membuat API FileUploader. Dimana akan menyimpan field seperti id, file_path, file_name, size, owner dll dalam database. Lihat contoh model di bawah ini:

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)

Sekarang, Untuk API inilah yang saya inginkan:

  1. DAPATKAN:

Ketika saya mengaktifkan titik akhir GET, saya ingin semua bidang di atas untuk setiap file yang diunggah.

  1. POS:

Tetapi bagi pengguna untuk membuat / mengunggah file, mengapa dia harus khawatir tentang melewati semua bidang ini. Dia bisa mengunggah file dan kemudian, saya kira, serializer bisa mendapatkan sisa bidang dari FILE yang diunggah.

Searilizer: Pertanyaan: Saya membuat serializer di bawah ini untuk memenuhi tujuan saya. Tetapi tidak yakin apakah itu cara yang tepat untuk menerapkannya.

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)

Viewset untuk referensi:

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
Jadav Bheda
sumber
Logika validasi apa FileUploaderSerializer.validateyang dikandung metode?
x-yuri
6

Jika ada yang tertarik dengan contoh termudah dengan ModelViewset for Django Rest Framework.

Modelnya adalah,

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'

The Serializer,

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

Dan Pandangannya adalah,

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

Uji di Postman,

masukkan deskripsi gambar di sini

sadat
sumber
Dan bagaimana kami bisa mengirim permintaan menggunakan ajax. Apa sebenarnya imageUrl itu?
Eduard Grigoryev
imageUrl adalah file dalam permintaan.
sadat
1
def post(self,request):
        serializer = ProductSerializer(data=request.DATA, files=request.FILES)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
Syed Faizan
sumber
0

Dalam django-rest-framework, data permintaan diurai oleh Parsers.
http://www.django-rest-framework.org/api-guide/parsers/

Secara default django-rest-framework menggunakan kelas parser JSONParser. Ini akan mengurai data menjadi json. jadi, file tidak akan diurai dengannya.
Jika kita ingin file diurai bersama dengan data lain kita harus menggunakan salah satu kelas parser di bawah ini.

FormParser
MultiPartParser
FileUploadParser
anjaneyulubatta505
sumber
Pada versi saat ini dari DRF 3.8.2, itu akan mengurai secara default application/json, application/x-www-form-urlencodeddan multipart/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)
sidhu Munagala
sumber
0

Saya ingin menulis opsi lain yang menurut saya lebih bersih dan lebih mudah dirawat. Kami akan menggunakan defaultRouter untuk menambahkan URL CRUD untuk kumpulan tampilan kami dan kami akan menambahkan satu lagi url tetap yang menentukan tampilan pengunggah dalam kumpulan tampilan yang sama.

**** 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 utama proyek

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

.- BACA AKU.

Keajaiban terjadi ketika kita menambahkan @action decorator ke metode kelas 'uploader' kita. Dengan menetapkan argumen "method = ['put']", kita hanya mengizinkan permintaan PUT; sempurna untuk mengunggah file.

Saya juga menambahkan argumen "parser_classes" untuk menunjukkan bahwa Anda dapat memilih parser yang akan mengurai konten Anda. Saya menambahkan CSVParser dari paket rest_framework_csv, untuk mendemonstrasikan bagaimana kami hanya dapat menerima jenis file tertentu jika fungsi ini diperlukan, dalam kasus saya, saya hanya menerima "Jenis Konten: teks / csv". Catatan: Jika Anda menambahkan Parser kustom, Anda harus menetapkannya di parsers_classes di ViewSet karena permintaan akan membandingkan media_type yang diizinkan dengan parser utama (class) sebelum mengakses parser metode uploader.

Sekarang kita perlu memberitahu Django bagaimana pergi ke metode ini dan dimana bisa diimplementasikan di url kita. Saat itulah kami menambahkan url tetap (Tujuan sederhana). Url ini akan mengambil argumen "nama file" yang akan diteruskan dalam metode nanti. Kita perlu meneruskan metode ini "uploader", menentukan protokol http ('PUT') dalam daftar ke metode PostsViewSet.as_view.

Saat kita mendarat di url berikut

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

itu akan mengharapkan permintaan PUT dengan tajuk yang menentukan "Jenis-Konten" dan Isi-Disposisi: lampiran; filename = "sesuatu.csv".

curl -v -u user:pass http://example.com/posts/uploader/ --upload-file ./something.csv --header "Content-type:text/csv"
Wolfgang Leon
sumber
Jadi Anda menyarankan untuk mengunggah file, lalu melampirkannya ke beberapa data db. Bagaimana jika kemelekatan tidak pernah terjadi karena alasan tertentu? Mengapa tidak melakukannya dalam satu permintaan? parser_classestidak ada batasan file mana yang dapat diunggah. Ini memungkinkan Anda memutuskan format mana yang dapat digunakan untuk membuat permintaan. Setelah dipikir-pikir, cara Anda menangani unggahan ... sepertinya Anda memasukkan data dari CSV ke dalam database. Bukan apa yang diminta OP.
x-yuri
@ x-yuri dengan mengatakan "CSV adalah file" dan pertanyaannya adalah; Bagaimana cara memeriksa apakah ada data dalam permintaan? Dengan menggunakan metode ini, Anda akan menemukan data di request.data. _data = request.data karena PUT sedang digunakan. Seperti yang Anda katakan, parser_classes ada untuk memutuskan format mana yang DAPAT digunakan untuk membuat permintaan karenanya dengan menggunakan format lain yang TIDAK Anda inginkan, kemudian akan dikecualikan dengan menambahkan lapisan keamanan tambahan. Apa yang Anda lakukan dengan data Anda terserah Anda. Menggunakan "Coba Kecuali" Anda dapat memeriksa apakah "melampirkan tidak pernah terjadi" yang tidak diperlukan, bukan itu yang dilakukan oleh kode. Ini dibuat dalam 1 permintaan
Wolfgang Leon
0

Ini adalah salah satu pendekatan yang saya terapkan semoga akan membantu.

     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)
Harshit Trivedi
sumber
0

Anda dapat menggeneralisasi jawaban @ Nithin untuk bekerja secara langsung dengan sistem serializer DRF yang ada dengan membuat kelas parser untuk mengurai bidang tertentu yang kemudian dimasukkan langsung ke serializers DRF standar:

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

Ini digunakan seperti:

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

Jika Anda menggunakan ModelViewSet, sebenarnya Anda sudah selesai! Ia menangani setiap hal untuk Anda! Anda hanya perlu meletakkan field di ModelSerializer Anda dan mengaturnya content-type=multipart/form-data;di klien Anda.

TAPI seperti yang Anda ketahui, Anda tidak dapat mengirim file dalam format json. (ketika tipe konten disetel ke application / json di klien Anda). Kecuali Anda menggunakan format Base64.

Jadi, Anda punya dua pilihan:

  • biarkan ModelViewSetdan ModelSerializertangani pekerjaan itu dan kirim permintaan menggunakancontent-type=multipart/form-data;
  • setel bidang ModelSerializersebagai Base64ImageField (or) Base64FileFielddan beri tahu klien Anda untuk menyandikan file ke Base64dan menyetelcontent-type=application/json
Hojat Modaresi
sumber
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')

Kirim permintaan posting ke api/filesdengan file Anda dilampirkan ke form-databidang file. File akan diupload ke /mediafolder dan record db akan ditambahkan dengan id dan nama file.

Achala Dissanayake
sumber