Django REST Framework 和 FileField 绝对 url

问题描述 投票:0回答:11

我定义了一个简单的 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 表示中图像的完整 url。

我认为我需要在 ProjectSerializer 中创建一个自定义字段,但没有成功。

python django url filefield
11个回答
60
投票

尝试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})

12
投票

谢谢,剃须疣猪。您的示例和文档参考有很大帮助。我的实现略有不同,但与您发布的非常接近:

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') 

7
投票

要获取使用 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 }}

6
投票

我发现为序列化方法字段编写相同的代码很烦人。 如果您已正确设置 S3 存储桶 URL 的

MEDIA_ROOT
,则可以向序列化器添加一个字段,例如:

class ProjectSerializer(serializers.ModelSerializer):
    logo_url = serializers.URLField(read_only=True, source='logo.url')

    class Meta:
        model = Project

logo 是模型中的一个 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 生成的。这不是媒体。


6
投票

无需任何覆盖或自定义。 DRF 自动处理它。看看

to_representation
FileField
方法(source):

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
,不用担心,一切都会默默完成,但如果您手动实例化序列化器,则必须在上下文中传递请求。

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


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}

1
投票

只需传递此

"context={'request': request}"
参数即可调用模型序列化器类来序列化对象。您可以按照以下代码片段获取完整的 URL 字段。

serialized_object = serializers.MySerializer(data, many=true, context={'request': request})

0
投票

检查设置.py

媒体设置。

我有同样的错误并发现:

MEDIA_URL = '/media/' 成功了。

之前我只有:

MEDIA_URL = '媒体/'


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"
    }
]

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
.

的表示

0
投票

如果您无法使用 Viewsets 访问序列化器中的额外上下文,请尝试使用 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)

© www.soinside.com 2019 - 2024. All rights reserved.