2

Similar questions have been asked here:

But the answer has always been the same ("use .save() instead") - this solution does work, but because of signals on the model which I can't control, I would like to know if there are any work-arounds that can be used instead.

# serializers.py

from drf_extra_fields.fields import Base64ImageField
from rest_framework import serializers


class ProfileSerializer(serializers.Serializer):
    email = serializers.CharField(required=False, allow_null=True)
    image = Base64ImageField(required=False, allow_null=True, max_length=None, use_url=True)

# views.py

def get_as_base64(url: AnyStr):
    if url is None:
        return None, None
    full_name = url.split("/")[-1]
    my_base = base64.b64encode(requests.get(url).content)
    return my_base, full_name

def do_profile_update(profile: Dict):
    b64_image, file_name = get_as_base64(profile.get("url"))
    if b64_image and file_name:
        *_, file_ext = file_name.split(".")
        b64_image = f"data:image/{file_ext};base64,{b64_image.decode('utf-8')}"

    build_dict = {
        "email": profile.get("email"),
        "image": b64_image,
    }
    serializer = ProfileSerializer(data=build_dict)
    serializer.is_valid()  # returns True
    # serializer.validated_data = {'email': 'test@example.org', 'image': <SimpleUploadedFile: 11735acf-6d96-4c62-9e6a-8ebb7f7ea509.jpg (image/jpeg)>}
    Profile.objects.filter(pk=pk).update(**serializer.validated_data)  # This saves the *name* of the image to the database, but no image file

I understand that a ImageField is basically a CharField, and I can see the name of the image in the database, but the image isn't written to the server.

My question boils down to: is there a way to separately write the image to the server, retrieve the path to the newly saved image, and use it in the .update()?

TYIA.

kunambi
  • 756
  • 1
  • 10
  • 25

1 Answers1

0

You can save your file separately to the media server with django's DefaultStorage class (see docs).

import os
import io
import requests
from django.conf import settings
from django.core.files.images import ImageFile
from django.core.files.storage import default_storage

response = requests.get(url)
full_name = url.split("/")[-1]
img_path = os.path.join(settings.MEDIA_ROOT, full_name)
image = ImageFile(io.BytesIO(response.content))
default_storage.save(img_path, image)
movileanuv
  • 350
  • 1
  • 12
  • Where/how do I run the `.update()`, and what do should I update with - the `image` or `img_path`? – kunambi Aug 23 '21 at 18:25
  • The thing is, I don't really know what the Base64ImageField does. It seems to me it only saves the base64 image as a string in the database, and it doesn't actually write it to the disk. That base64 string is enough to render your image in the frontend. Your question was how to upload the file to the server. If it was a regular ImageField, you woud have updated it with the `full_name`, since django already knows where `MEDIA_ROOT` is. – movileanuv Aug 27 '21 at 06:09