Hochladen der Django Rest Framework-Datei

97

Ich verwende Django Rest Framework und AngularJs, um eine Datei hochzuladen. Meine Ansichtsdatei sieht folgendermaßen aus:

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)

Da die letzte Zeile der Post-Methode alle Daten zurückgeben sollte, habe ich mehrere Fragen:

  • wie man prüft, ob etwas drin ist request.FILES ?
  • Wie serialisiere ich ein Dateifeld?
  • Wie soll ich Parser verwenden?
Pawan
quelle
8
NUR EIN HINWEIS ZU DEN MODS: Django hat seit 2013 ein starkes Upgrade durchgeführt. Wenn also jemand anderes jetzt dieselbe Frage stellt. BITTE nicht abschießen ^ _ ^.
Jessi
Wie wäre es mit Base64?
Hojat Modaresi

Antworten:

67

Verwenden Sie den FileUploadParser , alles ist in der Anfrage enthalten. Verwenden Sie stattdessen eine Put-Methode. Ein Beispiel finden Sie in den Dokumenten :)

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)
erfreut nicht dazu
quelle
12
@pleasedontbelong warum wurde hier die PUT-Methode anstelle von POST verwendet?
Md. Tanvir Raihan
7
hi @pleasedontbelong, wenn es einen neuen Datensatz erstellt, wäre es stattdessen POST? und funktioniert es weiterhin mit FileUploadParser?
Nuttynibbles
1
@pleasedontbelong RTan stellt eine ziemlich gute Frage. Das Lesen von RFC-2616 bietet eine Subtilität, die mir bisher nicht bewusst war. "Der grundlegende Unterschied zwischen den POST- und PUT-Anforderungen spiegelt sich in der unterschiedlichen Bedeutung des Anforderungs-URI wider. Der URI in einer POST-Anforderung identifiziert die Ressource, die die eingeschlossene Entität verarbeitet. Diese Ressource kann ein Datenakzeptanzprozess sein, ein Gateway zu einem anderen Protokoll oder einer separaten Entität, die Anmerkungen akzeptiert. Im Gegensatz dazu identifiziert der URI in einer PUT-Anforderung die Entität, die der Anforderung beigefügt ist "
dudeman
2
Warum FileUploadParser? "Der FileUploadParser ist für die Verwendung mit nativen Clients vorgesehen, die die Datei als Rohdatenanforderung hochladen können. Für webbasierte Uploads oder für native Clients mit Unterstützung für mehrteilige Uploads sollten Sie stattdessen den MultiPartParser-Parser verwenden." Scheint im Allgemeinen keine gute Option zu sein. Außerdem sehe ich keine Datei-Uploads, die einer bestimmten Behandlung bedürfen .
X-Yuri
3
Zum zweiten @ x-yuri beschwert sich DRF darüber, dass der Content-Disposition-Header leer ist, wenn ich den FileUploadParser verwende. MultiPartParser ist viel einfacher, da nur angenommen wird, dass der Dateiname der angegebene Dateiname in den Formularfeldern ist.
David Zwart
74

Ich verwende denselben Stapel und habe auch nach einem Beispiel für das Hochladen von Dateien gesucht, aber mein Fall ist einfacher, da ich das ModelViewSet anstelle von APIView verwende. Der Schlüssel stellte sich als pre_save-Hook heraus. Am Ende habe ich es zusammen mit dem Angular-File-Upload-Modul wie folgt verwendet:

# 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
quelle
11
pre_save ist in drf 3.x
Guy S
Nach meiner Erfahrung ist für Dateifelder keine besondere Behandlung erforderlich .
X-Yuri
Die Methoden @ Guy-S, perform_create, perform_update, perform_destroy ersetzen die nicht mehr verfügbaren Methoden pre_save, post_save, pre_delete und post_delete der alten Version 2.x: django-rest-framework.org/api-guide/generic-views / # Methoden
Rufat
36

Endlich kann ich mit Django ein Bild hochladen. Hier ist mein Arbeitscode

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-Anfrage zum Hochladen

curl -X POST -S -H -u "admin:password" -F "[email protected];type=image/jpg" 127.0.0.1:8000/resourceurl/imageUpload
Vipul J.
quelle
14
Warum wird destination.close () innerhalb der for-Schleife platziert?
Makerj
12
Es scheint besser zu sein with open('/Users/Username/' + up_file.name, 'wb+') as destination:, den Verschluss vollständig zu verwenden und zu entfernen
Chuck Wilbur
Es ist einfacher zu bedienen ModelViewSet. Außerdem haben sie es höchstwahrscheinlich besser umgesetzt.
X-Yuri
Ich habe mich den ganzen Tag auf diese Antwort verlassen ... bis ich herausfand, dass wenn Sie mehrere Dateien hochladen möchten, dies nicht FileUploadParsererforderlich ist, aber MultiPartParser!
Olivier Pons
12

Nachdem ich einen Tag damit verbracht hatte, stellte ich fest, dass ...

Für jemanden, der eine Datei hochladen und einige Daten senden muss, gibt es keine direkte Möglichkeit, sie zum Laufen zu bringen. Hierfür gibt es ein offenes Problem in den JSON-API-Spezifikationen. Eine Möglichkeit, die ich gesehen habe, ist die Verwendung multipart/relatedwie hier gezeigt , aber ich denke, es ist sehr schwierig, sie in drf zu implementieren.

Schließlich hatte ich implementiert, die Anfrage als zu senden formdata. Sie würden jede Datei als Datei und alle anderen Daten als Text senden . Jetzt haben Sie zwei Möglichkeiten, um die Daten als Text zu senden. Fall 1) Sie können jede Daten als Schlüsselwertpaar senden oder Fall 2) Sie können einen einzelnen Schlüssel namens Daten haben und den gesamten JSON als Wertzeichenfolge senden.

Die erste Methode funktioniert sofort, wenn Sie einfache Felder haben, ist jedoch ein Problem, wenn Sie verschachtelte Serialisierungen haben. Der mehrteilige Parser kann die verschachtelten Felder nicht analysieren.

Im Folgenden stelle ich die Implementierung für beide Fälle bereit

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 -> Es sind keine besonderen Änderungen erforderlich. Mein Serializer wird hier aufgrund der beschreibbaren Implementierung von ManyToMany Field nicht als zu lang angezeigt.

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'

Wenn Sie nun der ersten Methode folgen und nur Nicht-Json-Daten als Schlüsselwertpaare senden, benötigen Sie keine benutzerdefinierte Parser-Klasse. DRF'd MultipartParser erledigt den Job. Aber für den zweiten Fall oder wenn Sie verschachtelte Serializer haben (wie ich gezeigt habe), benötigen Sie einen benutzerdefinierten Parser wie unten gezeigt.

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)

Dieser Serializer würde grundsätzlich jeden JSON-Inhalt in den Werten analysieren.

Das Anforderungsbeispiel in Postman für beide Fälle: Fall 1 Fall 1 ,

Fall 2 case2

Nithin
quelle
Ich möchte lieber Fall 2 vermeiden. Das Erstellen eines Datenbankeintrags pro Anforderung sollte die meiste Zeit in Ordnung sein.
X-Yuri
sehr hilfreich vielen Dank. Aber ich verstehe nicht, warum Sie Diktatdaten im Parser in QueryDict konvertieren? In meinem Fall in Django funktionieren normale Wörterbuchdaten perfekt ohne Konvertierung.
Metehan Gülaç
Ich habe ein anderes Szenario mit der von Ihnen genannten Antwort ausprobiert und es funktioniert erfolgreich. Sie können auf meine Antwort schauen .
Metehan Gülaç
7

Ich habe dieses Problem mit ModelViewSet und ModelSerializer gelöst. Hoffe das wird der Community helfen.

Ich bevorzuge auch die Validierung und die Anmeldung von Object-> JSON (und umgekehrt) im Serializer selbst und nicht in Ansichten.

Verstehen wir es anhand eines Beispiels.

Angenommen, ich möchte eine FileUploader-API erstellen. Hier werden Felder wie ID, Dateipfad, Dateiname, Größe, Eigentümer usw. in der Datenbank gespeichert. Siehe Beispielmodell unten:

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)

Für APIs möchte ich Folgendes:

  1. BEKOMMEN:

Wenn ich den GET-Endpunkt starte, möchte ich alle oben genannten Felder für jede hochgeladene Datei.

  1. POST:

Damit der Benutzer eine Datei erstellen / hochladen kann, muss er sich darum kümmern, alle diese Felder zu übergeben. Sie kann einfach die Datei hochladen und dann kann der Serializer vermutlich die restlichen Felder aus der hochgeladenen DATEI abrufen.

Searilizer: Frage: Ich habe unten einen Serializer erstellt, um meinen Zweck zu erfüllen. Aber nicht sicher, ob es der richtige Weg ist, es zu implementieren.

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 als Referenz:

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
quelle
Welche Validierungslogik enthält die FileUploaderSerializer.validateMethode?
X-Yuri
7

Nach meiner Erfahrung müssen Sie nichts Besonderes an Dateifeldern tun, sondern müssen nur das Dateifeld verwenden:

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

und Sie können Dateien hochladen:

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

Fügen Sie -F field=valuefür jedes zusätzliche Feld Ihres Modells hinzu. Und vergessen Sie nicht, die Authentifizierung hinzuzufügen.

x-yuri
quelle
3

Wenn jemand an dem einfachsten Beispiel mit ModelViewset für Django Rest Framework interessiert ist.

Das Modell ist,

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'

Der Serializer,

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

Und die Ansicht ist,

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

Test im Postboten,

Geben Sie hier die Bildbeschreibung ein

Sadat
quelle
0

Im Django-Rest-Framework werden Anforderungsdaten von der analysiert Parsers.
http://www.django-rest-framework.org/api-guide/parsers/

Standardmäßig verwendet das django-rest-Framework die Parser-Klasse JSONParser. Die Daten werden in json analysiert. Daher werden Dateien nicht damit analysiert.
Wenn Dateien zusammen mit anderen Daten analysiert werden sollen, sollten wir eine der folgenden Parser-Klassen verwenden.

FormParser
MultiPartParser
FileUploadParser
anjaneyulubatta505
quelle
Auf der aktuellen Version von DRF 3.8.2, wird es standardmäßig analysieren application/json, application/x-www-form-urlencodedund 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
quelle
0
def post(self,request):
        serializer = ProductSerializer(data=request.DATA, files=request.FILES)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
Syed Faizan
quelle
0

Ich möchte eine weitere Option schreiben, die meiner Meinung nach sauberer und einfacher zu warten ist. Wir werden den defaultRouter verwenden, um CRUD-URLs für unser Viewset hinzuzufügen, und wir werden eine weitere feste URL hinzufügen, die die Uploader-Ansicht innerhalb desselben Viewset angibt.

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

Haupt-URLs des Projekts

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

.- LIESMICH.

Die Magie entsteht, wenn wir unserer Klassenmethode 'Uploader' @action decorator hinzufügen. Durch Angabe des Arguments "Methods = ['put']" werden nur PUT-Anforderungen zugelassen. Perfekt zum Hochladen von Dateien.

Ich habe auch das Argument "parser_classes" hinzugefügt, um zu zeigen, dass Sie den Parser auswählen können, der Ihren Inhalt analysiert. Ich habe CSVParser aus dem Paket rest_framework_csv hinzugefügt, um zu demonstrieren, wie wir nur bestimmte Dateitypen akzeptieren können, wenn diese Funktionalität erforderlich ist. In meinem Fall akzeptiere ich nur "Content-Type: text / csv". Hinweis: Wenn Sie benutzerdefinierte Parser hinzufügen, müssen Sie diese in parsers_classes im ViewSet angeben, da die Anforderung den zulässigen Medientyp mit Hauptparsern (Klassenparsern) vergleicht, bevor Sie auf die Parser der Uploader-Methode zugreifen.

Jetzt müssen wir Django sagen, wie er zu dieser Methode gehen soll und wo sie in unseren URLs implementiert werden kann. Dann fügen wir die feste URL hinzu (einfache Zwecke). Diese URL verwendet ein "Dateinamen" -Argument, das später in der Methode übergeben wird. Wir müssen diese Methode "Uploader" übergeben und das http-Protokoll ('PUT') in einer Liste an die PostsViewSet.as_view-Methode angeben.

Wenn wir in der folgenden URL landen

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

Es wird eine PUT-Anforderung mit Headern erwartet, in denen "Inhaltstyp" und Inhaltsdisposition angegeben sind: Anhang; Dateiname = "etwas.csv".

curl -v -u user:pass http://example.com/posts/uploader/ --upload-file ./something.csv --header "Content-type:text/csv"
Wolfgang Leon
quelle
Sie schlagen also vor, eine Datei hochzuladen und sie dann an einen Datenbankdatensatz anzuhängen. Was ist, wenn das Anhängen aus irgendeinem Grund nie erfolgt? Warum nicht in einer Anfrage?parser_classesist nicht da, um zu begrenzen, welche Dateien hochgeladen werden können. Hier können Sie entscheiden, in welchen Formaten Anforderungen gestellt werden können. Auf den zweiten Blick, wie Sie mit dem Upload umgehen ... es scheint, als würden Sie Daten aus CSV in die Datenbank stellen. Nicht was OP gefragt hat.
X-Yuri
@ x-yuri mit den Worten "eine CSV ist eine Datei" und die Frage ist; Wie überprüfe ich, ob die Anfrage Daten enthält? Mit dieser Methode finden Sie die Daten in request.data. _data = request.data due PUT wird verwendet. Wie Sie sagten, sind parser_classes dazu da, zu entscheiden, welche Formate verwendet werden können, um eine Anfrage zu stellen, indem Sie ein anderes Format verwenden, das Sie NICHT wollen. Dann wird das Hinzufügen einer zusätzlichen Sicherheitsebene ausgeschlossen. Was Sie mit Ihren Daten machen, liegt bei Ihnen. Mit "Try Except" können Sie überprüfen, ob "Anhängen nie passiert", obwohl dies nicht erforderlich ist. Dies ist nicht das, was der Code tut. Diese werden in 1 Anfrage gemacht
Wolfgang Leon
0

Dies ist der Ansatz, den ich angewendet habe, hoffentlich hilft er.

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

Sie können die Antwort von @ Nithin verallgemeinern, um direkt mit dem vorhandenen Serializer-System von DRF zu arbeiten, indem Sie eine Parser-Klasse generieren, um bestimmte Felder zu analysieren, die dann direkt in die Standard-DRF-Serializer eingespeist werden:

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

Dies wird verwendet wie:

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

Wenn Sie ModelViewSet verwenden, sind Sie tatsächlich fertig! Es erledigt alles für Sie! Sie müssen nur das Feld in Ihren ModelSerializer einfügen und content-type=multipart/form-data;in Ihrem Client festlegen .

ABER wie Sie wissen, können Sie keine Dateien im JSON-Format senden. (Wenn der Inhaltstyp in Ihrem Client auf application / json eingestellt ist). Es sei denn, Sie verwenden das Base64-Format.

Sie haben also zwei Möglichkeiten:

  • Lassen ModelViewSetund ModelSerializerbearbeiten Sie den Job und senden Sie die Anfrage mitcontent-type=multipart/form-data;
  • Setzen Sie das Feld in ModelSerializeras Base64ImageField (or) Base64FileFieldund weisen Sie Ihren Client an, die Datei zu codieren Base64und zu setzencontent-type=application/json
Hojat Modaresi
quelle
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')

Senden Sie eine Post-Anfrage an api/filesmit einer an ein form-dataFeld angehängten Datei file. Die Datei wird in einen /mediaOrdner hochgeladen und ein Datenbankdatensatz mit ID und Dateiname hinzugefügt.

Achala Dissanayake
quelle