python - with - Django REST Framework+Django REST Swagger+ImageField
drf yasg documentation (3)
Creé un modelo simple con un ImageField y quiero hacer una vista de API con django-rest-framework + django-rest-swagger, que está documentado y es capaz de cargar el archivo.
Esto es lo que obtuve:
models.py
from django.utils import timezone
from django.db import models
class MyModel(models.Model):
source = models.ImageField(upload_to=u''/photos'')
is_active = models.BooleanField(default=False)
created_at = models.DateTimeField(default=timezone.now)
def __unicode__(self):
return u"photo {0}".format(self.source.url)
serializer.py
from .models import MyModel
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = [
''id'',
''source'',
''created_at'',
]
views.py
from rest_framework import generics
from .serializer import MyModelSerializer
class MyModelView(generics.CreateAPIView):
serializer_class = MyModelSerializer
parser_classes = (FileUploadParser, )
def post(self, *args, **kwargs):
"""
Create a MyModel
---
parameters:
- name: source
description: file
required: True
type: file
responseMessages:
- code: 201
message: Created
"""
return super(MyModelView, self).post(self, *args, **kwargs)
urls.py
from weddings.api.views import MyModelView
urlpatterns = patterns(
'''',
url(r''^/api/mymodel/$'', MyModelView.as_view()),
)
Para mí esto debería ser bastante simple. Sin embargo, no puedo hacer que la carga funcione. Siempre recibo esta respuesta de error:
He leído esta parte de la documentación de django-rest-framework :
If the view used with FileUploadParser is called with a filename URL keyword argument, then that argument will be used as the filename. If it is called without a filename URL keyword argument, then the client must set the filename in the Content-Disposition HTTP header. For example Content-Disposition: attachment; filename=upload.jpg.
Sin embargo, el encabezado se pasa por django-rest-swagger en la propiedad Solicitar carga útil (desde la consola de Chrome).
Si necesita más información, hágamelo saber.
Estoy usando Django==1.8.8
, djangorestframework==3.3.2
y django-rest-swagger==0.3.4
.
Lo hice funcionar haciendo un par de cambios en tu código.
Primero, en models.py
, cambie el nombre de ImageField
a file
y use la ruta relativa para cargar la carpeta. Cuando carga un archivo como transmisión binaria, está disponible en el diccionario request.data
bajo la clave de archivo ( request.data.get(''file'')
), por lo que la opción más limpia es asignarlo al campo modelo con el mismo nombre.
from django.utils import timezone
from django.db import models
class MyModel(models.Model):
file = models.ImageField(upload_to=u''photos'')
is_active = models.BooleanField(default=False)
created_at = models.DateTimeField(default=timezone.now)
def __unicode__(self):
return u"photo {0}".format(self.file.url)
En serializer.py
, cambie el nombre del campo de origen al archivo:
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = (''id'', ''file'', ''created_at'')
En views.py, no llame a super, sino que llame a create ():
from rest_framework import generics
from rest_framework.parsers import FileUploadParser
from .serializer import MyModelSerializer
class MyModelView(generics.CreateAPIView):
serializer_class = MyModelSerializer
parser_classes = (FileUploadParser,)
def post(self, request, *args, **kwargs):
"""
Create a MyModel
---
parameters:
- name: file
description: file
required: True
type: file
responseMessages:
- code: 201
message: Created
"""
return self.create(request, *args, **kwargs)
He usado la extensión Postman Chrome para probar esto. He subido imágenes como binarios y he configurado manualmente dos encabezados:
Content-Disposition: attachment; filename=upload.jpg
Content-Type: */*
Según mi experiencia, FileUploadParser
funciona con este formato de solicitud:
curl -X POST -H "Content-Type:multipart/form-data" /
-F "file=@{filename};type=image/jpg" /
https://endpoint.com/upload-uri/
El request.data[''file'']
en su vista tendrá el archivo.
Tal vez si prueba un Content-Type:multipart/form-data
, tendrá suerte.
Esta es la solución final que se me ocurrió:
from rest_framework import generics
from rest_framework.parsers import FormParser, MultiPartParser
from .serializer import MyModelSerializer
class MyModelView(generics.CreateAPIView):
serializer_class = MyModelSerializer
parser_classes = (FormParser, MultiPartParser)
def post(self, *args, **kwargs):
"""
Create a MyModel
---
parameters:
- name: source
description: file
required: True
type: file
responseMessages:
- code: 201
message: Created
"""
return super(MyModelView, self).post(self, *args, **kwargs)
Todo lo que tuve que hacer fue cambiar los analizadores de FileUploadParser
a (FormParser, MultiPartParser)