2

suppose that I want to return in a APIView.get a (non model) object conatining 2 properties, one of them is a model object, and the other is a binary Image

I've tried several ways and had problems with serializer.

thanks!

Serializer:

class MyCustomSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyCustom
        fields = '__all__'

View:

class MyCustomGet(APIView):
    def get(self, request, format=None):
        serializer = MyCustomSerializer
        obj = s.get_custom()
        return Response({"obj": serializer(obj.obj).data, "image": obj.image})

get_custom:

class CustomClass:
    obj = None
    image = None

def get_custom():
    r = CustomClass()
    r.obj = MyCustom.objects.all().first()
    r.image = PIL.Image.open('file/path.png')
    return r
1
  • 1
    You never mentioned what error you get? Commented Nov 18, 2017 at 2:19

3 Answers 3

4
+50

You are trying to render an image (binary data) in a JSON response (string). This would not work. If you want to pass the image in a JSON string, you have to encode it as a string and then decode it on the client side. An common example would be a base64-encoded string:

import io
import base64

...
def get_custom():
    ...
    image = PIL.Image.open('file/path.png')
    stream = io.StringIO()
    image.save(stream, format='PNG')
    return base64.b64encode(stream.getvalue())

Although not knowing anything about your REST endpoint design, a better solution in my opinion would be to declare a subresource with a separate view. Supposing you have a MyCustom resource in your REST schema accessible by api/mycustom/:id/ and served by MyCustomGet view, then e.g. a separate view could be responsible for offering the corresponding file under api/mycustom/:id/pic/:

import django.http

class MyCustomPicView(APIView):
    def get(self, request):
        ...
        image = PIL.Image.open('file/path.png')
        return django.http.HttpResponse(image, content_type='image/png')
Sign up to request clarification or add additional context in comments.

2 Comments

I've been thinking in base64 it but was not sure about sending a PIL Image Object, it impose a lot of restrictions in the client side, and before you post your answer I have opted by the api-call/:id/image solution, and it is working like a charm :)
@Qsebas glad I could confirm you're on the right track!
1

I'm not sure if this would be helpful, but this is what I did for a similar situation. I had one primary model object, and a semi-related object I wanted to tag in.

Basically, if your CustomClass model is somehow related to the image you need to include, just fetch it directly in the serializer instance.

This is sort of how I would try to do it:

class MyCustomSerializer(serializers.ModelSerializer):
    image_binary = serializers.SerializerMethodField()

    class Meta:
        model = MyCustom
        fields = '__all__'

    def get_image_binary(self, obj):
        return WhateverModel.objects.get(relation=obj).attribute

This simply adds some extra data to your model serializer, calculated on the fly by the serializer. You could even have a second serializer for the image_binary and in the get_image_binary() simply return the .data for the serializer, just like you did in your view.

This of course relies on the fact that the MyCustom object is somehow implicitly related to your image binary.

Comments

1

Firstly, in serializer you have mentioned model as MyCustom and your actual model name is CustomClass. Don't know if this is related or not. If image is the problem, you could convert the image to a string, and convert it back in the view/ wherever you want to use it.

class CustomClass:
    obj = None
    image = None

def get_custom():
    r = CustomClass()
    r.obj = MyCustom.objects.all().first()

    import base64

    with open("file/path.png", "rb") as imageFile:
        str_ = base64.b64encode(imageFile.read())
        r.image = str_

    return r

To convert it back to an image:

fh = open("imageToSave.png", "wb")
fh.write(r.image.decode('base64'))
fh.close()

I don't know what your specific use case is, but this structure doesn't look right. I would use an ImageField to store the image, and in the serializer i would simply return image.url.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.