Восстановление URL-адреса поля изображения Django/DRF из функции F()
У меня есть пример использования, где я пытаюсь переопределить URL изображения, если оно существует в нашей базе данных.
Вот секция набора запросов, из которой берется ImageField
через F()
запрос.
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()),
)
Case-When разрешается правильно, но проблема в том, что значение, возвращаемое из F("agent__profile__profile_photo")
, не является URL, который может быть использован пользовательским интерфейсом. Вместо этого это что-то вроде:
"agentphoto/09bd7dc0-62f6-49ab-blah-6c57b23029d7/profile/1665342685--77e51d9c5asdf345364f774d0b2def48.jpeg"
Обычно я получаю URL через agent.profile.profile_photo.url
, но при попытке выполнить preferred_profile_photo.url
я получаю следующее:
AttributeError: 'str' object has no attribute 'url'
.
Я пробовал обернуть в Value(..., output_field=ImageField())
безрезультатно.
Суть заключается в получении url из ImageField после преобразования из F()
Для справки, я использую storages.backends.s3boto3.S3Boto3Storage
.
Благодарю за любую помощь!
Django не хранит полный URL в БД. Он строит абсолютный URL на уровне кода. Цитата из docs:
Все, что будет храниться в вашей базе данных, это путь к файлу (относительно MEDIA_ROOT).
Для получения полного URL ваших файлов вы можете использовать метод build_absolute_uri()
. Например, вы можете сделать это на уровне сериализатора:
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)
Я смог реализовать это, используя некоторую информацию из этого поста.
Вот вспомогательная функция, которую я использовал для реализации:
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)