2013-12-09 16 views
41

Ich verwende Django Rest Framework und AngularJs, um eine Datei hochzuladen. Meine Ansicht Datei sieht wie folgt aus:Django Rest Framework Datei hochladen

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 sollte alle Daten zurückgeben, habe ich mehrere Fragen:

  • , wie Sie überprüfen, ob es etwas in request.FILES ist?
  • Wie serialisiert man das Dateifeld?
  • Wie soll ich Parser verwenden?

Antwort

31

Verwenden Sie die FileUploadParser, es ist alles in der Anfrage. eine Put-Methode Verwenden Sie stattdessen, werden Sie ein Beispiel in der Dokumentation finden :)

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

hey, weißt du, wie ich http://stackoverflow.com/questions/26673572/django-rest-framework-upload-file-to-a-method lösen könnte? – psychok7

+2

@pleasedontbelong Warum wurde hier die PUT-Methode anstelle von POST verwendet? – RTan

+0

@Rego überprüfen Sie diese http://StackOverflow.com/A/14402607/361427 :) – pleasedontbelong

45

ich den gleichen Stapel bin mit und auch ein Beispiel für Datei-Upload anschaute, aber mein Fall ist einfacher, da ich Verwenden Sie das ModelViewSet anstelle von APIView. Der Schlüssel erwies sich als pre_save hook. Ich landete es zusammen mit dem Winkel-Datei-Upload-Modul wie folgt:

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

Danke! Eine ausgezeichnete (und sehr vollständige) Referenz! – WhyNotHugo

+3

pre_save ist in drf 3.x –

18

Schließlich ich in der Lage bin Bild zu Django. Hier ist meine Arbeits Code

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 Anfrage hochladen

curl -X POST -S -H -u "admin:password" -F "[email protected];type=image/jpg" 127.0.0.1:8000/resourceurl/imageUpload 
+11

veraltet, warum destination.close() innerhalb der for-Schleife platziert wird? – makerj

+4

Scheint es besser zu sein '' open ('/ Users/Username /' + up_file.name, 'wb +') als Ziel zu verwenden: 'und entferne den close ganz –

2

ich dieses Problem mit ModelViewSet und ModelSerializer gelöst. Hoffe, das wird der Gemeinschaft helfen.

Ich bevorzuge auch Validierung und Objekt-> JSON (und umgekehrt) Login in Serializer selbst und nicht in Ansichten.

Lasst uns das mit einem Beispiel verstehen.

Sagen wir, ich möchte FileUploader API erstellen. Dort werden Felder wie ID, Dateipfad, Dateiname, Größe, Besitzer usw. in der Datenbank gespeichert. Siehe Beispielmodell unter:

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) 

Nun Für APIs das ist, was ich will:

  1. GET: Wenn ich die GET-Endpunkt Feuer, ich will alle oben genannten Bereichen für jede hochgeladene Datei.

  2. POST: Aber für Benutzer zum Erstellen/Hochladen von Datei, warum sie sich darum kümmern muss, alle diese Felder zu übergeben. Sie kann einfach die Datei hochladen, und dann kann der Serializer die restlichen Felder von der hochgeladenen Datei abrufen.

Searilizer: Frage: ich unter Serializer erstellt meinen Zweck zu dienen. 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 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 
0

In django-Rest-Rahmen Anforderungsdaten werden durch den Parsers geparst.
http://www.django-rest-framework.org/api-guide/parsers/

standardmäßig django-rest-Framework nimmt Parserklasse JSONParser. Es wird die Daten in json analysieren. Dateien werden daher nicht analysiert.
Wenn wir wollen, dass Dateien zusammen mit anderen Daten analysiert werden, sollten wir eine der folgenden Parser-Klassen verwenden.

FormParser 
MultiPartParser 
FileUploadParser 
Verwandte Themen