Melissa Stewart
Melissa Stewart

Reputation: 3605

Can't upload image and get expecteds behavior in django rest framework

I've tried all possible options on this over the last two days and nothing seems to work. Please bear with me this will be a little long.

This is my UserProfile model

class UserProfile(models.Model):
    user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE, )
    status = models.CharField(max_length=255, null=True, blank=True)
    thumbnail = models.ImageField(upload_to='media/user/', blank=True, null=True)

This is the Serializer,

class UserProfileSerializer(serializers.ModelSerializer):
    thumbnail = serializers.ImageField(max_length=256, use_url=True, allow_empty_file=False)

    class Meta:
        model = models.UserProfile
        fields = ('status', 'thumbnail',)

Now when I try to upload this image using POSTMAN, this the output that I get,

{
    "thumbnail": [
        "No file was submitted."
    ]
}

Reading some SO posts I tried replacing the default serializer.ImageField to the this,

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

        # 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

Nothing changes and I continue to get the exact same behaviour. The output that I'm expecting is something like this.

{
  "status":"something",
  "thumbnail":"file/path"
}

This is how I'm uploading an image using POSTMAN.

enter image description here

Can someone please help me with this, I've pretty much tried out every option on SO to no avail.

Upvotes: 2

Views: 1422

Answers (2)

Badjio
Badjio

Reputation: 760

I'd recommend to use multipart/form-data to upload the image along with your other json data, something like this:

# create a custom image field with additional constraints e.g. size of the image
class ConstrainedImageField(ImageField):
    MAX_SIZE = 2000000  # in Bytes
    default_error_messages = {
        'image_size': _('The size of the image is {image_size} KB. The maximum size allowed is: {max_size} KB.'),
    }

    def to_internal_value(self, data):
        super(ConstrainedImageField, self).to_internal_value(data=data)
        file_size = data.size
        if file_size > self.MAX_SIZE:
            max_size_kb = self.MAX_SIZE/1000
            file_size_kb = file_size/1000
            self.fail('image_size', max_size=max_size_kb, image_size=file_size_kb)



class UserProfileSerializer(serializers.ModelSerializer):
    thumbnail = serializers.ConstrainedImageField(max_length=256, use_url=True, allow_empty_file=False)

    class Meta:
        model = models.UserProfile
        fields = ('status', 'thumbnail',)

    def validate(self, data):
        # ..
        # ..

        # get the image data from request.FILES:
        self.context["thumbnail"] = self.context['request'].FILES.get("thumbnail")
        return data

    def create(self, validated_data):
        # set the thumbnail field:
        validated_data['thumbnail'] = self.context.get("thumbnail")
        user_profile = UserProfile.objects.create(**validated_data)
        return user_profile

Upvotes: 2

SHIVAM JINDAL
SHIVAM JINDAL

Reputation: 2974

You should send base64 encoded image data (not file upload) via postman (in case, you are using Base64ImageField)

You can refer this also: Error uploading image using postman in django rest framework

How to send a base64 image in postman

Process of getting encoded image

  1. Use Image encoder for image encoding. After image upload, you will have an option of copy image.
  2. Copy the data and paste it in and .txt file
  3. You will get an string with prefix 'data:image/png;base64,'
  4. Remove that prefix from string and paste the whole string in POSTMAN

Upvotes: 2

Related Questions