Resolve Django/DRF ImageField URL from F() function

I have a use case where I'm attempting to override an Image URL if it exists in our database.

Here is the section of the queryset that is grabbing the ImageField from via an F() query.

preferred_profile_photo=Case(
            When(
                # agent not exists
                Q(agent__id__isnull=False),
                then=F("agent__profile__profile_photo"),
            ),
            default=Value(None, output_field=ImageField()),
        )

The Case-When is resolving correctly, but the issue is the value returns from F("agent__profile__profile_photo") is not the URL than can be used by the UI. Instead it is something like: "agentphoto/09bd7dc0-62f6-49ab-blah-6c57b23029d7/profile/1665342685--77e51d9c5asdf345364f774d0b2def48.jpeg"

Typically, I'd retrieve the URL via agent.profile.profile_photo.url, but I receive the following when attempting to perform preferred_profile_photo.url: AttributeError: 'str' object has no attribute 'url'.

I've tried wrapping in Value(..., output_field=ImageField()) with no luck.

The crux here is retrieving the url from the ImageField after resolving from F()

For reference, I'm using storages.backends.s3boto3.S3Boto3Storage.

Appreciate any help!

Django doesn't store full URL in DB. It builds absolute url on code level. Quote from docs:

All that will be stored in your database is a path to the file (relative to MEDIA_ROOT).

You can use build_absolute_uri() method to get full URL of your files. For example you can do it on serializer level:

class YourSerializer(ModelSerializer):
    preferred_profile_photo = serializers.SerializerMethodField()

    class Meta:
        model = YourModel
        fields = [
            'preferred_profile_photo',
        ]

    def get_preferred_profile_photo(self, obj):
        request = self.context.get('request')
        return request.build_absolute_uri(obj.preferred_profile_photo)

I was able to implement this using some information from this post.

Here is the helper function I used to implement:

from django.core.files.storage import get_storage_class
def get_media_storage_url(file_name: str) -> str:
    """Takes the postgres stored ImageField value and converts it to
    the proper S3 backend URL. For use cases, where calling .url on the
    photo field is not feasible.

    Args:
        file_name (str): the value of the ImageField

    Returns:
        str: the full URL of the object usable by the UI
    """
    media_storage = get_storage_class()()
    return media_storage.url(name=file_name)
Back to Top