我已经定义了一个包含以下模型的简单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,我定义了一个名为ProjectSerializer的HyperlinkedModelSerializer后代:
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"
}
我还没有弄清楚如何在项目的JSON表示中提供一个包含完整网址的缩略图字段。
我认为我需要在ProjectSerializer中创建一个自定义字段,但还没有成功。
答案 0 :(得分:42)
示例(未经测试):
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})
答案 1 :(得分:7)
谢谢,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')
答案 2 :(得分:6)
要获取使用FileField的文件的url,您只需调用FieldFile的url属性(这是文件实例而不是字段),它使用Storage类来确定此文件的URL。如果您使用的是Amazon S3等外部存储,或者存储发生变化,则非常简单。
get_thumbnail_url会是这样的。
def get_thumbnail_url(self, obj):
return obj.thumbnail.url
您也可以这样在模板中使用它:
{{ current_project.thumbnail.url }}
答案 3 :(得分:3)
我发现为序列化方法字段编写相同的代码很烦人。
如果您已将MEDIA_ROOT
正确设置为S3存储桶URL,则可以向序列化程序添加字段,如:
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
来返回在我的API中使用其他视图的绝对URL。例如,在我的项目中有一个URL /webviews/projects/<pk>
,它显示了一个标题和一个按钮,它收集了一些用户输入(即不完全是你用后缀做什么,因为它不是资源的简单表示,但包括而是一些逻辑)。结束点/projects/<pk>/
包含一个字段“webview_url”,它是使用SerializerMethodField生成的。这不是媒体。
答案 4 :(得分:3)
无需任何替代或自定义。 DRF会自动处理它。看看to_representation
的{{1}}方法:
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
https://www.django-rest-framework.org/community/3.0-announcement/#file-fields-as-urls
答案 5 :(得分:1)
检查settings.py
媒体设置。
我有同样的错误并发现:
MEDIA_URL ='/ media /' 做了伎俩。
在我之前:
MEDIA_URL ='media /'
答案 6 :(得分:1)
只需传递上下文并传递请求对象即可。如果您使用的是@api_view
serializer = CustomerSerializer(customer, context={"request": request})
对于ViewSet用户get_serializer_context方法
class ProjectViewSet(viewsets.ModelViewSet):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
def get_serializer_context(self):
return {'request': self.request}
答案 7 :(得分:1)
只需传递此"context={'request': request}"
参数,即可在其中调用模型序列化程序类以序列化对象。您可以按照以下代码段获取完整的URL字段。
serialized_object = serializers.MySerializer(data, many=true, context={'request': request})
答案 8 :(得分:0)
就我而言,重写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"
}
]
答案 9 :(得分:0)
检查此!
class FileFieldWithLinkRepresentation(serializers.FileField):
def to_representation(self, value):
return create_link(value.url, self.context['request'])
和create_link
方法:
def create_link(path: str, request: Request):
domain = request.META['HTTP_HOST']
if not path.startswith('/', 0, 1):
path = '/' + path
return request.scheme + "://" + domain + path
您可以在每个需要超链接的类中使用FileFieldWithLinkRepresentation
FileField
的表示形式。
答案 10 :(得分:0)
如果您无法使用Viewset在Seriralizer中访问额外的上下文,请尝试在urls.py中使用基本名称注册路由器:
router.register('projects', ProjectViewSet, basename='project')
您可以使用build_absolute_uri:
def get_thumbnail_url(self, obj):
return self.context.get('request').build_absolute_uri(obj.thumbnail.url)