queryset framework español create python django angularjs django-rest-framework

python - español - django rest framework serializer queryset



Django Rest Framework File Upload (6)

Estoy usando Django Rest Framework y AngularJs para subir un archivo. Mi archivo de vista se ve así:

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)

Como la última línea del método de publicación debe devolver todos los datos, tengo varias preguntas:

  • cómo verificar si hay algo en la request.FILES ¿ request.FILES ?
  • cómo serializar el campo de archivo?
  • ¿cómo debo usar el analizador?

Después de pasar 1 día en esto, descubrí que ...

Para alguien que necesita cargar un archivo y enviar algunos datos, no hay una forma directa de hacerlo funcionar. Hay un problema abierto en las especificaciones json api para esto. Una posibilidad que he visto es usar multipart/related como se muestra here , pero creo que es muy difícil implementarlo en drf.

Finalmente, lo que había implementado era enviar la solicitud como formdata . Enviaría cada archivo como archivo y todos los demás datos como texto. Ahora, para enviar los datos como texto, tiene dos opciones. caso 1) puede enviar cada dato como par de valor clave o caso 2) puede tener una sola clave llamada datos y enviar todo json como cadena en valor.

El primer método funcionaría de forma predeterminada si tiene campos simples, pero será un problema si ha anidado serializaciones. El analizador multiparte no podrá analizar los campos anidados.

A continuación, proporciono la implementación para ambos casos

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 -> no se necesitan cambios especiales, no se muestra mi serializador aquí porque es demasiado extenso debido a la implementación escribible en ManyToMany Field.

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''

Ahora, si está siguiendo el primer método y solo está enviando datos que no son de Json como pares clave de valores, no necesita una clase de analizador personalizado. DRF''d MultipartParser hará el trabajo. Pero para el segundo caso o si tiene serializadores anidados (como he mostrado) necesitará un analizador personalizado como se muestra a continuación.

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)

Este serializador básicamente analizaría cualquier contenido JSON en los valores.

El ejemplo de solicitud en post man para ambos casos: caso 1 ,

Caso 2


En django-rest-framework los datos de solicitud son analizados por los Parsers .
http://www.django-rest-framework.org/api-guide/parsers/

Por defecto, django-rest-framework toma la clase de análisis JSONParser . Analizará los datos en json. por lo tanto, los archivos no se analizarán con él.
Si queremos que los archivos se analicen junto con otros datos, deberíamos usar una de las siguientes clases de analizador.

FormParser MultiPartParser FileUploadParser


Estoy usando la misma pila y también estaba buscando un ejemplo de carga de archivos, pero mi caso es más sencillo ya que uso ModelViewSet en lugar de APIView. La clave resultó ser el gancho pre_save. Terminé usándolo junto con el módulo de carga angular de archivos, así:

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


Finalmente puedo subir imágenes usando Django. Aquí está mi código de trabajo

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 request para subir

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


Resolví este problema con ModelViewSet y ModelSerializer. Espero que esto ayude a la comunidad.

También prefiero tener la validación y Object-> JSON (y viceversa) iniciar sesión en el serializador en lugar de en las vistas.

Permite entenderlo por ejemplo.

Digamos, quiero crear la API FileUploader. Donde estará almacenando campos como id, file_path, file_name, size, owner, etc. en la base de datos. Vea el modelo de muestra a continuación:

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)

Ahora, para las API esto es lo que quiero:

  1. GET: Cuando disparo el punto final GET, quiero todos los campos anteriores para cada archivo cargado.

  2. POST: Pero para que el usuario cree / cargue archivos, ¿por qué tiene que preocuparse por pasar todos estos campos? Ella solo puede cargar el archivo y luego, supongo, el serializador puede obtener el resto de los campos del ARCHIVO cargado.

Searilizer: Pregunta: Creé el serializador a continuación para cumplir con mi propósito. Pero no estoy seguro si es la forma correcta de 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 para referencia:

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


Use FileUploadParser , todo está en la solicitud. Use un método put en su lugar, encontrará un ejemplo en los documentos :)

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)