2013-12-09 30 views
41

Sto utilizzando Django Rest Framework e AngularJs per caricare un file. Il mio file vista simile a questo:Upload file quadro resto Django

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) 

Come l'ultima riga del metodo POST dovrebbe restituire tutti i dati, ho diverse domande:

  • Come controllare se c'è qualcosa in request.FILES?
  • come serializzare il campo del file?
  • come dovrei usare parser?

risposta

31

Utilizzare lo FileUploadParser, è tutto nella richiesta. Utilizzare un metodo put, invece, troverete un esempio nella documentazione :)

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

Ehi, ti so come ho potuto risolvere http://stackoverflow.com/questions/26673572/django-rest-framework-upload-file-to-a-method? – psychok7

+2

@pleasedontbelong perché il metodo PUT è stato utilizzato qui invece di POST? – RTan

+0

@Rego controlla questo http://stackoverflow.com/a/14402607/361427 :) – pleasedontbelong

45

sto usando lo stesso stack ed è stato anche alla ricerca di un esempio di caricamento di file, ma il mio caso è più semplice da quando ho utilizzare ModelViewSet invece di APIView. La chiave si è rivelata il gancio pre_save. Ho finito per usare insieme con il modulo angolare di caricamento file in questo modo:

# 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

Grazie! Un riferimento eccellente (e molto completo)! – WhyNotHugo

+3

pre_save è deprecato in drf 3.x –

18

Infine sono in grado di caricare un'immagine utilizzando Django. Qui è il mio codice di lavoro

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

richiesta curl per caricare

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

perché destination.close() è inserito all'interno del ciclo for? – makerj

+4

Sembra che sarebbe meglio usare 'con open ('/ Users/Username /' + up_file.name, 'wb +') come destinazione:' e rimuovi completamente chiudi –

2

ho risolto questo problema con ModelViewSet e ModelSerializer. Spero che questo aiuti la comunità.

Ho anche il privilegio di avere la convalida e l'accesso Object-> JSON (e viceversa) nello stesso serializzatore anziché nelle viste.

Lo capiamo con l'esempio.

Dire, voglio creare l'API FileUploader. Dove memorizzerà campi come id, percorso_file, nome_file, dimensione, proprietario ecc. Nel database. Vedi modello di esempio:

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) 

Ora, per le API questo è quello che voglio:

  1. GET: Quando sparo l'endpoint GET, voglio tutti i campi di cui sopra per ogni file caricato.

  2. POST: Ma per l'utente per creare/caricare file, perché deve preoccuparsi di passare tutti questi campi. Può solo caricare il file e quindi, suppongo, il serializzatore può ottenere il resto dei campi dal FILE caricato.

Searilizer: Domanda: ho creato sotto serializzatore per servire il mio scopo. Ma non sono sicuro se è il modo giusto per implementarlo.

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 per riferimento:

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-resto-quadro di dati richiesta viene analizzato dal Parsers.
http://www.django-rest-framework.org/api-guide/parsers/

Per impostazione predefinita django-rest-framework accetta la classe parser JSONParser. Analizzerà i dati in json. quindi, i file non verranno analizzati con esso.
Se vogliamo analizzare i file insieme ad altri dati, dovremmo usare una delle seguenti classi di parser.

FormParser 
MultiPartParser 
FileUploadParser 
Problemi correlati