with tutorial integrar framework espaƱol con python angularjs django django-rest-framework

python - tutorial - integrar angularjs con django



Imagen de carga de Django REST Framework: "Los datos enviados no eran un archivo" (2)

El problema que está enfrentando es que el marco REST de Django espera que los archivos se carguen como datos de formularios de varias partes , a través de los métodos estándar de carga de archivos. Este suele ser un campo de file , pero el objeto Blob JavaScript también funciona para AJAX.

Está buscando cargar los archivos con una cadena codificada en base64, en lugar del archivo sin formato, que no es compatible de manera predeterminada . Hay implementaciones de un Base64ImageField por ahí , pero la más prometedora vino por una solicitud de extracción .

Dado que estos fueron diseñados principalmente para Django REST framework 2.x, mejoré el de la solicitud de extracción y creé uno que debería ser compatible con DRF 3.

serializers.py

from rest_framework import serializers class Base64ImageField(serializers.ImageField): """ A Django REST framework field for handling image-uploads through raw post data. It uses base64 for encoding and decoding the contents of the file. Heavily based on https://github.com/tomchristie/django-rest-framework/pull/1268 Updated for Django REST framework 3. """ def to_internal_value(self, data): from django.core.files.base import ContentFile import base64 import six import uuid # Check if this is a base64 string if isinstance(data, six.string_types): # Check if the base64 string is in the "data:" format if ''data:'' in data and '';base64,'' in data: # Break out the header from the base64 content header, data = data.split('';base64,'') # Try to decode the file. Return validation error if it fails. try: decoded_file = base64.b64decode(data) except TypeError: self.fail(''invalid_image'') # Generate file name: file_name = str(uuid.uuid4())[:12] # 12 characters are more than enough. # Get the file name extension: file_extension = self.get_file_extension(file_name, decoded_file) complete_file_name = "%s.%s" % (file_name, file_extension, ) data = ContentFile(decoded_file, name=complete_file_name) return super(Base64ImageField, self).to_internal_value(data) def get_file_extension(self, file_name, decoded_file): import imghdr extension = imghdr.what(file_name, decoded_file) extension = "jpg" if extension == "jpeg" else extension return extension

Esto debería usarse en reemplazo del ImageField estándar proporcionado por el marco REST de Django. Entonces tu serializador se convertiría

class ImageSerializer(serializers.ModelSerializer): image = Base64ImageField( max_length=None, use_url=True, ) class Meta: model = Image fields = ("id", ''image'', ''owner'', ''time_created'', )

Esto debería permitirle especificar una cadena codificada en base64 o el objeto Blob estándar que el marco REST de Django normalmente espera.

Me estoy inclinando a cargar archivos en Django, y aquí encuentro un problema que debería ser trivial, con el error:

Los datos enviados no eran un archivo. Verifique el tipo de codificación en el formulario.

Debajo está el detalle.

Nota: También miré en Django Rest Framework ImageField , y probé

serializer = ImageSerializer(data=request.data, files=request.FILES)

pero consigo

TypeError: __init__() obtuvo un argumento de palabra clave inesperado ''archivos''

Tengo un modelo de Image que me gustaría interactuar a través del marco Django REST:

modelos.py

class Image(models.Model): image = models.ImageField(upload_to=''item_images'') owner = models.ForeignKey( User, related_name=''uploaded_item_images'', blank=False, ) time_created = models.DateTimeField(auto_now_add=True)

serializers.py

class ImageSerializer(serializers.ModelSerializer): image = serializers.ImageField( max_length=None, use_url=True, ) class Meta: model = Image fields = ("id", ''image'', ''owner'', ''time_created'', )

settings.py

''DEFAULT_PARSER_CLASSES'': ( ''rest_framework.parsers.JSONParser'', ''rest_framework.parsers.FormParser'', ''rest_framework.parsers.MultiPartParser'', ),

El front-end (usando AngularJS y angular-restmod o $resource ) envía datos JSON con el owner y la image del formulario:

Entrada:

{"owner": 5, "image": "data:image/jpeg;base64,/9j/4QqdRXhpZgAATU0A..."}

En el backend, request.data muestra

{u''owner'': 5, u''image'': u''data:image/jpeg;base64,/9j/4QqdRXhpZgAATU0AKgAAA..."}

Pero entonces ImageSerializer(data=request.data).errors muestra el error

ReturnDict([(''image'', [u''The submitted data was not a file. Check the encoding type on the form.''])])

¿Me pregunto qué debo hacer para corregir el error?

EDITAR: parte JS

Los códigos frontales relacionados constan de dos partes: una directive angular-file-dnd (disponible here ) para colocar el archivo en la página y angular-restmod , que proporciona operaciones CRUD:

<!-- The template: according to angular-file-dnd, --> <!-- it will store the dropped image into variable $scope.image --> <div file-dropzone="[image/png, image/jpeg, image/gif]" file="image" class=''method'' data-max-file-size="3" file-name="imageFileName"> <div layout=''row'' layout-align=''center''> <i class="fa fa-upload" style=''font-size:50px;''></i> </div> <div class=''text-large''>Drap & drop your photo here</div> </div> # A simple `Image` `model` to perform `POST` $scope.image_resource = Image.$build(); $scope.upload = function() { console.log("uploading"); $scope.image_resource.image = $scope.image; $scope.image_resource.owner = Auth.get_profile().user_id; return $scope.image_resource.$save(); };

Una actualización sobre el problema: en este momento cambié a usar ng-file-upload , que envía datos de imagen en el formato adecuado.


Me encontré con el mismo problema hace unos días. Aquí está mi vista del marco de descanso django para manejar la carga de archivos

views.py

class PhotoUploadView(APIView): parser_classes = (FileUploadParser,) def post(self, request): user = self.request.user if not user: return Response(status=status.HTTP_403_FORBIDDEN) profile = None data = None photo = None file_form = FileUploadForm(request.POST,request.FILES) if file_form.is_valid(): photo = request.FILES[''file''] else: return Response(ajax_response(file_form),status=status.HTTP_406_NOT_ACCEPTABLE) try: profile = Organizer.objects.get(user=user) profile.photo = photo profile.save() data = OrganizersSerializer(profile).data except Organizer.DoesNotExist: profile = Student.objects.get(user=user) profile.photo = photo profile.save() data = StudentsSerializer(profile).data return Response(data)

En el front-end, utilicé lib de angular-file-upload .

Aquí está mi entrada de archivo

<div ng-file-drop="" ng-file-select="" ng-model="organizer.photo" class="drop-box" drag-over-class="{accept:''dragover'', reject:''dragover-err'', delay:100}" ng-multiple="false" allow-dir="true" accept="image/*"> Drop Images or PDFs<div>here</div> </div>

Y aquí está mi servicio de carga

main.js

(function () { ''use strict''; angular .module(''trulii.utils.services'') .factory(''UploadFile'', UploadFile); UploadFile.$inject = [''$cookies'', ''$http'',''$upload'',''$window'',''Authentication'']; /** * @namespace Authentication * @returns {Factory} */ function UploadFile($cookies, $http,$upload,$window,Authentication) { /** * @name UploadFile * @desc The Factory to be returned */ var UploadFile = { upload_file: upload_file, }; return UploadFile; function upload_file(file) { return $upload.upload({ url: ''/api/users/upload/photo/'', // upload.php script, node.js route, or servlet url //method: ''POST'' or ''PUT'', //headers: {''Authorization'': ''xxx''}, // only for html5 //withCredentials: true, file: file, // single file or a list of files. list is only for html5 //fileName: ''doc.jpg'' or [''1.jpg'', ''2.jpg'', ...] // to modify the name of the file(s) //fileFormDataName: myFile, // file formData name (''Content-Disposition''), server side request form name // could be a list of names for multiple files (html5). Default is ''file'' //formDataAppender: function(formData, key, val){} // customize how data is added to the formData. // See #40#issuecomment-28612000 for sample code }) } } })();