I can't save the image to the image field.
Error
"image": [
"No file was submitted."
]
models.py
class MyImages(models.Model):
name = models.CharField(max_length=255)
image = models.ImageField(upload_to='myphoto', null=False, max_length=255, blank=False)
views.py
class ImageList(APIView):
parser_classes = (MultiPartParser, FileUploadParser,)
def post(self, request):
file_serializer = MyImageSerializer(data=request.data)
if file_serializer.is_valid():
file_serializer.save()
return Response(file_serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serialiser.py
class MyImageSerializer(serializers.ModelSerializer):
class Meta:
model = MyImages
fields = ('id', 'name', 'image')
when using postman for file upload the file name is returned in view.py instead of the file object.
I have also seen these threads but not working
1 2
FileUploadParser populates request.data with a file key containing uploaded file.
However, ImageSerializer needs to serialize image this from the request.
Ergo, you need to specify fields explicitly. e.g.
class ImageFileField(serializers.Field):
def to_representation(self, obj):
return obj.image.url
def to_internal_value(self, data):
return data['file']
class MyImageSerializer(serializers.ModelSerializer):
name = serializers.CharField()
image = ImageFileField(source='*')
class Meta:
model = MyImages
I couldn't find any bug in your code when I tried to reproduce the error.
Make sure that uncheck the application/json content type
Then the postman console will be as follows,
Related
I am hoping that I can find a way to resize an uploaded image file before it is put into the database.
I am new to Django with REST, so I am not sure how this would be done. It seems that whatever is serialized is just kind of automatically railroaded right into the model. Which I suppose is the point (it's certainly an easy thing to setup).
To clarify, I already have a function tested and working that resizes the image for me. That can be modified as needed and is no problem for me. The issue really is about sort of "intercepting" the image, making my changes, and then putting it into the model. Could someone help me out with some ideas of tactics to get that done? Thanks.
The Model:
class Media(models.Model):
objects = None
username = models.ForeignKey(User, to_field='username',
related_name="Upload_username",
on_delete=models.DO_NOTHING)
date = models.DateTimeField(auto_now_add=True)
media = models.FileField(upload_to='albumMedia', null=True)
file_type = models.CharField(max_length=12)
MEDIA_TYPES = (
('I', "Image"),
('V', "Video")
)
media_type = models.CharField(max_length=1, choices=MEDIA_TYPES, default='I')
user_access = models.CharField(max_length=1, choices=ACCESSIBILITY, default='P')
class Meta:
verbose_name = "MediaManager"
The View with post method:
class MediaView(APIView):
queryset = Media.objects.all()
parser_classes = (MultiPartParser, FormParser)
permission_classes = [permissions.IsAuthenticated, ]
serializer_class = MediaSerializer
def post(self, request, *args, **kwargs):
user = self.request.user
print(user.username)
request.data.update({"username": user.username})
media_serializer = MediaSerializer(data=request.data)
# media_serializer.update('username', user.username)
if media_serializer .is_valid():
media_serializer.save()
return Response(media_serializer.data, status=status.HTTP_201_CREATED)
else:
print('error', media_serializer.errors)
return Response(media_serializer.errors,status=status.HTTP_400_BAD_REQUEST)
The Serializer:
class MediaSerializer(serializers.ModelSerializer):
class Meta:
model = Media
fields = '__all__'
def to_representation(self, instance):
data = super(MediaSerializer, self).to_representation(instance)
return data
You can use validate method to validate and/or change the values from data dictionary.
class MediaSerializer(serializers.ModelSerializer):
...
def validate(self, data):
value_from_form = data['value_from_form']
value_from_form = 'Something else'
data['value_from_form'] = value_from_form
return data
I'm trying to make an image upload through a REST API from a mobile client. I've managed to implement it using an multipart request to the REST endpoint, but when I try to update the image, the request is not handled correctly because of the constraints on the OneToOneField.
This is how I implemented the API:
models.py
class Hotel(models.Model):
name = models.CharField(max_length=500, null=False, blank=False)
address = models.CharField(max_length=500, null=False, blank=False)
rating = models.FloatField()
owner = models.CharField(max_length=200, null=False)
class Meta:
ordering = ['name']
class HotelPhoto(models.Model):
photo = models.ImageField(upload_to='hotel_photos', null=True)
hotel = models.OneToOneField(Hotel, on_delete=models.CASCADE, primary_key=True)
views.py
class HotelPhotoUpload(APIView):
parser_classes = [FormParser, MultiPartParser]
def post(self, request):
photo_serializer = HotelPhotoSerializer(data=request.data,
context={'request': request})
if photo_serializer.is_valid():
photo_serializer.save()
return Response(photo_serializer.data,
status=status.HTTP_201_CREATED)
else:
logger.error(f'Error uploading image: {photo_serializer.errors}')
return Response(photo_serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
serializers.py
class HotelSerializer(serializers.HyperlinkedModelSerializer):
photo = serializers.ImageField(source='hotelphoto.photo', read_only=True)
class Meta:
model = Hotel
fields = ['url', 'id', 'name', 'address', 'rating', 'owner', 'photo']
class HotelPhotoSerializer(serializers.ModelSerializer):
photo_url = serializers.SerializerMethodField()
class Meta:
model = HotelPhoto
fields = ['hotel', 'photo', 'photo_url']
def get_photo_url(self, obj):
return self.context['request'].build_absolute_uri(obj.photo.url)
This is the error I'm getting:
Error uploading image: {'hotel': [ErrorDetail(string='hotel photo with this hotel already exists.', code='unique')]}
Bad Request: /hotels/photo/upload/
I understand this is due to the constraint on the OneToOneField since I've have already uploaded a photo, but how should I do the request in order to just update the HotelPhoto.photo field?
What I've tried
Implementing a put method on the HotelPhotoUpload view with partial=True on the serializer, but it gave the same error.
I thought about overwriting the validate method on the serializer, but I don't know if I need to validate anything on the photo itself. I was hoping the framework would handle this for me.
Thought about merging the HotelPhoto and Hotel models, but that would require a big refactor of other code.
EDIT:
I'm currently using django 3.0.2.
Following the answer by neferpitou, I've managed to get it working after these minor changes:
serializers.py
# Didn't change the HotelSerializer
class HotelPhotoSerializer(serializers.ModelSerializer):
hotel = serializers.PrimaryKeyRelatedField(
many=False,
queryset=Hotel.objects.all())
class Meta:
model = HotelPhoto
fields = ['hotel', 'photo']
def create(self, validated_data):
# Instead of creating a new HotelPhoto instance
# changed the photo field from the Hotel instance
hotel = validated_data.get('hotel')
photo = validated_data.get('photo')
hotel.hotelphoto.photo.save(photo.name, photo)
hotel.save()
return hotel.hotelphoto
views.py
class HotelPhotoUpload(APIView):
parser_classes = [FormParser, MultiPartParser]
def post(self, request):
# I'm already sending the hotel id on the POST request
photo_serializer = HotelPhotoSerializer(data=request.data)
if photo_serializer.is_valid():
photo_serializer.save()
return Response(photo_serializer.data,
status=status.HTTP_201_CREATED)
else:
logger.error(f'Error uploading image: {photo_serializer.errors}')
return Response(photo_serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
One thing that I forgot to mention is that I'm always sending a request (either POST or PUT) to the hotels endpoint (which uses the HotelSerializer) before uploading a photo. So I'm not expecting to have problems on the photo/upload endpoint due to an inexistent hotel.
mobile client
Unfortunaly can't post the Multipart POST request content because it's huge. But here is the client method implementation using Retrofit 2.5.0.
// Sends the hotel id and the entire content of the photo file.
#Multipart
#POST(UPLOAD_ENDPOINT)
fun uploadPhoto(#Part("hotel") hotelId: RequestBody,
#Part photo: MultipartBody.Part) : Call<UploadResult>
companion object {
const val UPLOAD_ENDPOINT = "hotels/photo/upload/"
}
Foreignkey and OneToOneField can be serialized in the same manner.
Here is your
views.py
class HotelPhotoUpload(APIView):
# parser_classes = [FormParser, MultiPartParser]
def post(self, request):
hotel = Hotel.objects.get(name=request.data.get('hotel'))
request.data['hotel'] = hotel.id
photo_serializer = HotelPhotoSerializer(data=request.data)
# print(photo_serializer)
if photo_serializer.is_valid():
photo_serializer.save()
return Response(photo_serializer.data,
status=status.HTTP_201_CREATED)
else:
# logger.error(f'Error uploading image: {photo_serializer.errors}')
return Response(photo_serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
serializers.py
class HotelSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Hotel
fields = '__all__'
class HotelPhotoSerializer(serializers.ModelSerializer):
hotel = serializers.PrimaryKeyRelatedField(many=False, queryset=Hotel.objects.all())
class Meta:
model = HotelPhoto
fields = ['hotel', 'photo',]
def create(self, validated_data):
hotel_photo = HotelPhoto.objects.create(**validated_data)
hotel_photo.save()
return hotel_photo
I am confused why there are extra fields in your HotelSerializer, so I have trimmed it down. If you have specific use case for those feel free to modify in your code. And there are no primary key in your Hotel model, so it will create id field by default and I am assuming every hotel name in unique.
Postman request:
Hotel Data From Admin Section:
I'm trying to build django rest api which will get images from front part and also serve it via REST APi, the problem is that eventho I'm able to post data to django via PostMan, still I get error Object of type 'Logo' is not JSON serializable. Data is already in database but I would like to fix this issue but dont know where to start, tried to change Response to JsonResponse but it brought no effect
Model
class Logo(models.Model):
name = models.CharField(max_length=60)
preview = models.ImageField()
thumb = models.ImageField()
thumbL = models.ImageField()
dataL = models.TextField(blank=False)
thumbS = models.ImageField()
dataS = models.TextField()
posL = models.CharField(max_length=60)
posS = models.CharField(max_length=60)
def __str__(self):
return str(self.name)
Serialization:
class LogoSerializer(serializers.ModelSerializer):
posL = serializers.CharField(required=False)
posS = serializers.CharField(required=False)
dataL = serializers.CharField(required=False)
dataS = serializers.CharField(required=False)
class Meta:
model = Logo
fields = ('__all__')
def create(self, validated_data):
return Logo.objects.create(**validated_data)
and View:
class LogoViewSet(viewsets.ViewSet):
parser_classes = (MultiPartParser, FormParser)
def list(self, request, *args, **kwargs):
queryset = Logo.objects.all()
serializer = LogoSerializer(queryset, many=True)
return Response(serializer.data)
def post(self, request, *args, **kwargs):
serializer = LogoSerializer(data=request.data)
if serializer.is_valid():
serializer.validated_data['posL'] = positionL
serializer.validated_data['posS'] = positionS
bg = serializer.save()
return Response(bg, status=status.HTTP_201_CREATED)
else:
bg = serializer.errors
return Response(bg, status=status.HTTP_400_BAD_REQUEST)
According to the docs you should use serializer.data instead of bg in your response.
So return Response(serializer.data, status=status.HTTP_201_CREATED)
The reason for this is because you are passing a Django model instance, which doesn't really have the fields in raw JSON serializable format, but rather a more complex object with field ins the format of CharFields, ImageFields, as well as a meta property that defines some of the model's properties like database table, etc.
I am trying to perform a POST request to create an article and I am getting this error Request failed with status code 400 Bad Request: /api/articles/create/.
An article needs 3 attributes to be created:
(1) title
(2) body
(3) author (the current user)
The router works fine since the POST request goes into the post method of the ArticleCreateView class. But I'm guessing that serializer.is_valid() is returning False for some reason.
Also print(request.data) returns {'title': 'hello', 'body': 'stuff'}
Another mystery about Django is, how does the serializer know if I want to get, create or update something? In all the examples I've seen, the serializer magically seems to know this.
class ArticleCreateView(CreateAPIView):
permission_classes = [IsAuthenticated]
def post(self, request):
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
def create(self, validated_data):
author = self.context['request'].user
title = validated_data.get('title')
body = validated_data.get('body')
return Article.objects.create(author=author, title=title, body=body)
class Article(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
body = models.TextField(max_length=100)
date_updated = models.DateTimeField(auto_now=True)
date_created = models.DateTimeField(auto_now_add=True)
According to your serializer, the validation process needs author details, which is not passing through POST payload.
So, make the author field in the serializer a not required field by adding required=False or use a ReadOnlyField() or specify read_only_fields in Meta class. Also pass context data as #nishant mentioned
# views.py
class ArticleCreateView(CreateAPIView):
permission_classes = [IsAuthenticated]
def post(self, request):
serializer = ArticleSerializer(data=request.data, context={"request": request}) # change here
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
# serializer.py
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
read_only_fields = ('author',)
def create(self, validated_data):
author = self.context['request'].user
title = validated_data.get('title')
body = validated_data.get('body')
return Article.objects.create(author=author, title=title, body=body)
add this
serializer = ArticleSerializer(data=request.data, context={'request':request})
I am trying to make a simple api for marine vessels and ships and I am using django-rest-framework as the library to generate the api. With models that has simple fields like char fields and integers in the models level everything is working properly (create, delete, update, list, get).
The problem is with image fields whenever I try to post a record that has images it does not detect it. And it always causes an error "sequence item 1: expected string, NoneType found"
Below is my model, serializer, and view files.
serializer
class VesselSerializer(serializers.ModelSerializer):
image = serializers.ImageField(source='image')
class Meta:
model = Vessel
fields = ('image', 'id', 'vesselType', 'name')
class VesselTypeSerilizer(serializers.ModelSerializer):
class Meta:
model = VesselType
models
def vessel_file_name(instance, filename):
return '/'.join(['vessels', instance.id, filename]) #the error is in this line
class VesselType(models.Model):
name = models.CharField(max_length=50)
class Vessel(models.Model):
name = models.CharField(max_length=50)
vesselType = models.ForeignKey(VesselType)
image = models.ImageField(upload_to=vessel_file_name, null=True)
def __unicode__(self):
return u'%s' % self.name
views
class VesselList(generics.ListCreateAPIView):
queryset = Vessel.objects.all()
serializer_class = VesselSerializer
fields = ('url', 'id', 'image', 'vesselType')
def post(self, request, format=None):
print 'entered here'
print '%' * 10
print request.DATA
print request.FILES
print '%' * 10
serializer = VesselSerializer(data=request.DATA, files=request.FILES)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class VesselDetails(generics.RetrieveUpdateDestroyAPIView):
queryset = Vessel.objects.all()
serializer_class = VesselSerializer
It should be noted as well that the request.FILES and request.DATA when printed are displayed correctly yet that error appears which indicates that there is no file name although the name appears in request.FILES.
I am stuck in this problem for several hours and I can't seem to find what the problem is or what I am doing wrong. Any help would be appreciated.
The problem is that when vessel_file_name is called, the instance object is not saved to the db and instance.id is None.