There is a backend on django and a postgres DB that stores data about the item, including pictures. And there is a problem with getting a picture from the database, i've looked at enough solutions, but none of them work.
serializers.py
import base64
import uuid
import imghdr
class Base64ImageField(serializers.ImageField):
def to_internal_value(self, data):
if isinstance(data, str) and 'data:' in data and ';base64,' in data:
header, data = data.split(';base64,')
try:
decoded_file = base64.b64decode(data)
except (TypeError, ValueError):
self.fail('invalid_image')
file_name = str(uuid.uuid4())[:12]
file_extension = self.get_file_extension(file_name, decoded_file)
complete_file_name = f"{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):
extension = imghdr.what(file_name, decoded_file)
return extension or 'jpg'
class CreateItemSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
photo = Base64ImageField(required=True)
class Meta:
model = CreateItem
fields = '__all__'
def create(self, validated_data):
items = CreateItem.object.create_item(
name = validated_data.get('name'),
price = validated_data.get('price'),
description = validated_data.get('description'),
type_item = validated_data.get('type_item'),
photo=validated_data.get('photo')
)
return items
views.py
class GetItemView(APIView):
serializer_class = CreateItemSerializer
def get(self, request):
items = CreateItem.object.all()
response_data = {
'items': [
{
'photo': item.photo,
'name': item.name,
'description': item.description,
'type_item': item.type_item,
'price': item.price,
}
for item in items
]
}
return Response(response_data, status=status.HTTP_200_OK)
full error traceback
Internal Server Error: /api/v1/item/items-get/
Traceback (most recent call last):
File "/home/anton/Documents/e-m/e-market/backend/my_backend/venv_em/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "/home/anton/Documents/e-m/e-market/backend/my_backend/venv_em/lib/python3.12/site-packages/django/core/handlers/base.py", line 220, in _get_response
response = response.render()
^^^^^^^^^^^^^^^^^
File "/home/anton/Documents/e-m/e-market/backend/my_backend/venv_em/lib/python3.12/site-packages/django/template/response.py", line 114, in render
self.content = self.rendered_content
^^^^^^^^^^^^^^^^^^^^^
File "/home/anton/Documents/e-m/e-market/backend/my_backend/venv_em/lib/python3.12/site-packages/rest_framework/response.py", line 74, in rendered_content
ret = renderer.render(self.data, accepted_media_type, context)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/anton/Documents/e-m/e-market/backend/my_backend/venv_em/lib/python3.12/site-packages/rest_framework/renderers.py", line 100, in render
ret = json.dumps(
^^^^^^^^^^^
File "/home/anton/Documents/e-m/e-market/backend/my_backend/venv_em/lib/python3.12/site-packages/rest_framework/utils/json.py", line 25, in dumps
return json.dumps(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/json/__init__.py", line 238, in dumps
**kw).encode(obj)
^^^^^^^^^^^
File "/usr/lib/python3.12/json/encoder.py", line 200, in encode
chunks = self.iterencode(o, _one_shot=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/json/encoder.py", line 258, in iterencode
return _iterencode(o, 0)
^^^^^^^^^^^^^^^^^
File "/home/anton/Documents/e-m/e-market/backend/my_backend/venv_em/lib/python3.12/site-packages/rest_framework/utils/encoders.py", line 52, in default
return obj.decode()
^^^^^^^^^^^^
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte
[04/Oct/2024 18:59:30] "GET /api/v1/item/items-get/ HTTP/1.1" 500 115134
Maybe the problem is with the desirealizer, but if so I don't know how to fix it.
The error you're encountering is due to how the image data is handled in your serializer and view. The issue is that when you're trying to serialize the image field for the response, it's attempting to decode the binary image data as UTF-8 text, which is not correct. Let's modify your code to handle this properly
Serializers.py
from rest_framework import serializers
from django.core.files.base import ContentFile
import base64
import uuid
import imghdr
class Base64ImageField(serializers.ImageField):
def to_internal_value(self, data):
if isinstance(data, str) and 'data:' in data and ';base64,' in data:
header, data = data.split(';base64,')
try:
decoded_file = base64.b64decode(data)
except (TypeError, ValueError):
self.fail('invalid_image')
file_name = str(uuid.uuid4())[:12]
file_extension = self.get_file_extension(file_name, decoded_file)
complete_file_name = f"{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):
extension = imghdr.what(file_name, decoded_file)
return extension or 'jpg'
def to_representation(self, value):
if not value:
return None
return value.url # Return the URL of the image instead of the file object
class CreateItemSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
photo = Base64ImageField(required=True)
class Meta:
model = CreateItem
fields = '__all__'
def create(self, validated_data):
return CreateItem.objects.create(**validated_data)
views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import CreateItem
from .serializers import CreateItemSerializer
class GetItemView(APIView):
def get(self, request):
items = CreateItem.objects.all()
serializer = CreateItemSerializer(items, many=True)
return Response({'items': serializer.data}, status=status.HTTP_200_OK)