Django REST Framework и абсолютный URL-адрес FileField
Я определил простое приложение Django, которое включает следующую модель:
class Project(models.Model):
name = models.CharField(max_length=200)
thumbnail = models.FileField(upload_to='media', null=True)
(Технически да, это мог быть ImageField.)
В шаблоне достаточно просто включить значение MEDIA_URL (правильно закодированное в settings.py) в качестве префикса к URL-адресу миниатюр. Следующее прекрасно работает:
<div id="thumbnail"><img src="{{ MEDIA_URL }}{{ current_project.thumbnail }}" alt="thumbnail" width="400" height="300" border="0" /></div>
Используя DRF, я определил потомок HyperlinkedModelSerializer, называемый ProjectSerializer:
class ProjectSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Project
fields = ( 'id' ,'url', 'name', 'thumbnail')
И я определил очень простого потомка ModelViewSet:
class ProjectViewSet(viewsets.ModelViewSet):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
Образец результирующего JSON выглядит следующим образом:
{
"id": 1,
"url": "http://localhost:8000/api/v1/projects/1/",
"name": "Institutional",
"thumbnail": "media/institutional_thumb_1.jpg"
}
Мне еще не удалось выяснить, как предоставить поле миниатюр, которое включает полный URL-адрес изображения в моем представлении JSON проекта.
Я бы подумал, что мне нужно создать настраиваемое поле в ProjectSerializer, но не удалось.
Ответы
Ответ 1
Попробуйте SerializerMethodField
Пример (непроверенный):
class MySerializer(serializers.ModelSerializer):
thumbnail_url = serializers.SerializerMethodField('get_thumbnail_url')
def get_thumbnail_url(self, obj):
return self.context['request'].build_absolute_uri(obj.thumbnail_url)
Запрос должен быть доступен для сериализатора, поэтому он может создать полный абсолютный URL-адрес для вас. Один из способов - явно передать его, когда создается сериализатор, аналогично этому:
serializer = MySerializer(account, context={'request': request})
Ответ 2
Спасибо, shavenwarthog. Ваш пример и ссылка на документацию очень помогли. Моя реализация немного отличается, но очень близка к тому, что вы разместили:
from SomeProject import settings
class ProjectSerializer(serializers.HyperlinkedModelSerializer):
thumbnail_url = serializers.SerializerMethodField('get_thumbnail_url')
def get_thumbnail_url(self, obj):
return '%s%s' % (settings.MEDIA_URL, obj.thumbnail)
class Meta:
model = Project
fields = ('id', 'url', 'name', 'thumbnail_url')
Ответ 3
Чтобы получить URL-адрес файла, который использует FileField, вы можете просто вызвать атрибут url для FieldFile (это экземпляр файла, а не поле), он использует класс Storage для определения URL-адреса для этого файла. Это очень просто, если вы используете внешнее хранилище, такое как Amazon S3, или если ваше хранилище изменяется.
Это будет выглядеть как get_thumbnail_url.
def get_thumbnail_url(self, obj):
return obj.thumbnail.url
Вы также можете использовать его в шаблоне следующим образом:
{{ current_project.thumbnail.url }}
Ответ 4
Мне было неприятно писать один и тот же код для поля сериализованного метода.
Если вы правильно установили MEDIA_ROOT
на свой ведомый URL S3, вы можете добавить поле в сериализатор, например:
class ProjectSerializer(serializers.ModelSerializer):
logo_url = serializers.URLField(read_only=True, source='logo.url')
class Meta:
model = Project
логотип - это ImageField в модели. он не должен быть нулевым, чтобы избежать ошибок, таких как ValueError: The 'img' attribute has no file associated with it.
Я использую .build_absolute_uri
в поле метода serializer для возврата абсолютных URL-адресов, которые используют другие представления в моем API. например, в моем проекте есть URL /webviews/projects/<pk>
, который показывает название и кнопку, которая собирает некоторый пользовательский ввод (т.е. не совсем то, что вы будете делать с суффиксами, поскольку это не простое представление ресурса, но включает в себя некоторую логику вместо). конечная точка /projects/<pk>/
содержит поле "webview_url", которое создается с помощью SerializerMethodField. это не медиа.
Ответ 5
Проверьте настройки settings.py
настройки мультимедиа.
У меня была такая же ошибка и я обнаружил, что:
MEDIA_URL = '/media/'
сделал трюк.
До этого у меня только:
MEDIA_URL = 'media/'
Ответ 6
Просто передайте объект контекста и объекта запроса. если вы используете @api_view
serializer = CustomerSerializer(customer, context={"request": request})
Для пользователя метода get_serializer_context пользователя ViewSet
class ProjectViewSet(viewsets.ModelViewSet):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
def get_serializer_context(self):
return {'request': self.request}
Ответ 7
Не нужно никаких переопределений или настроек. DRF обрабатывает это автоматически. Посмотрите на to_representation
метод FileField
:
def to_representation(self, value):
if not value:
return None
use_url = getattr(self, 'use_url', api_settings.UPLOADED_FILES_USE_URL)
if use_url:
if not getattr(value, 'url', None):
# If the file has not been saved it may not have a URL.
return None
url = value.url
request = self.context.get('request', None)
if request is not None:
return request.build_absolute_uri(url)
return url
return value.name
Обратите внимание, что он не будет работать, если контекст сериализатора установлен неправильно. Если вы используете ViewSet
s, не беспокойтесь, все делается тихо, но если вы создаете экземпляр сериализатора вручную, вы должны передать запрос в контексте.
context = {'request': request}
serializer = ExampleSerializer(instance, context=context)
return Response(serializer.data)
https://www.django-rest-framework.org/community/3.0-announcement/#file-fields-as-urls
Ответ 8
В моем случае метод переопределения to_representation работает правильно.
# models.py
class DailyLove(models.Model):
content = models.CharField(max_length=1000)
pic = models.FileField(upload_to='upload/api/media/DailyLove/')
date = models.DateTimeField(auto_created=True)
def __str__(self):
return str(self.date)
# serializers.py
class DailyLoveSerializer(serializers.HyperlinkedModelSerializer):
def to_representation(self, instance):
representation = super(DailyLoveSerializer, self).to_representation(instance)
representation['pic_url'] = self.context['request'].build_absolute_uri('/' + instance.pic.url)
return representation
class Meta:
model = DailyLove
fields = '__all__'
# views.py
class DailyLoveViewSet(viewsets.ModelViewSet):
queryset = DailyLove.objects.all().order_by('-date')
serializer_class = DailyLoveSerializer
# result
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"url": "http://localhost:8088/daily/3/",
"date": "2019-05-04T12:33:00+08:00",
"content": "123",
"pic": "http://localhost:8088/daily/upload/api/media/DailyLove/nitish-meena-37745-unsplash.jpg",
"pic_url": "http://localhost:8088/upload/api/media/DailyLove/nitish-meena-37745-unsplash.jpg"
}
]