Python Django REST 框架和 FileField 绝对 url

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23918619/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 03:41:07  来源:igfitidea点击:

Django REST Framework and FileField absolute url

pythondjangourlfilefield

提问by Mark Semsel

I've defined a simple Django app that includes the following model:

我定义了一个简单的 Django 应用程序,其中包含以下模型:

class Project(models.Model):
    name = models.CharField(max_length=200)
    thumbnail = models.FileField(upload_to='media', null=True)

(Technically yes, that could have been an ImageField.)

(技术上是的,这可能是一个 ImageField。)

In a template, it's easy enough to include the MEDIA_URL value (duly coded in settings.py) as a prefix to the thumbnail URL. The following works fine:

在模板中,很容易将 MEDIA_URL 值(在 settings.py 中适当编码)作为缩略图 URL 的前缀。以下工作正常:

<div id="thumbnail"><img src="{{ MEDIA_URL }}{{ current_project.thumbnail }}" alt="thumbnail" width="400" height="300" border="0" /></div>

Using DRF, I've defined a HyperlinkedModelSerializer descendant called ProjectSerializer:

使用 DRF,我定义了一个名为 ProjectSerializer 的 HyperlinkedModelSerializer 后代:

class ProjectSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Project
        fields = ( 'id' ,'url', 'name', 'thumbnail')

And I've defined a very straightforward ModelViewSet descendant:

我已经定义了一个非常简单的 ModelViewSet 后代:

class ProjectViewSet(viewsets.ModelViewSet):
    queryset = Project.objects.all()
    serializer_class = ProjectSerializer

A sample of the resulting JSON looks like this:

生成的 JSON 示例如下所示:

{
    "id": 1, 
    "url": "http://localhost:8000/api/v1/projects/1/", 
    "name": "Institutional", 
    "thumbnail": "media/institutional_thumb_1.jpg"
}

I have not yet been able to figure out how to provide a thumbnail field that includes the full url to the image in my project's JSON representation.

我还没有弄清楚如何在我的项目的 JSON 表示中提供一个包含图像的完整 url 的缩略图字段。

I would think that I would need to create a custom field in the ProjectSerializer, but have not been successful.

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

采纳答案by johntellsall

Try SerializerMethodField

尝试SerializerMethodField

Example (untested):

示例(未经测试):

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)

The request must available to the serializer, so it can build the full absolute URL for you. One way is to explicitly pass it in when the serializer is created, similar to this:

该请求必须对序列化程序可用,因此它可以为您构建完整的绝对 URL。一种方法是在创建序列化程序时显式传入它,类似于:

serializer = MySerializer(account, context={'request': request})

回答by Mark Semsel

Thanks, shavenwarthog. Your example and documentation reference helped enormously. My implementation is slightly different, but very close to what you posted:

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

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

回答by Johnny Well

To get the url of a file which uses FileField you can just call the url attribute of the FieldFile (this is the file instance not the field), it use the Storage class to determine the url for this file. It's very straightforward if you are using a external storage like Amazon S3 or if your storage changes.

要获取使用 FileField 的文件的 url,您只需调用 FieldFile 的 url 属性(这是文件实例而不是字段),它使用 Storage 类来确定此文件的 url。如果您使用 Amazon S3 等外部存储或者您的存储发生变化,这将非常简单。

The get_thumbnail_url would be like this.

get_thumbnail_url 将是这样的。

def get_thumbnail_url(self, obj):
    return obj.thumbnail.url

You can also use it in the template this way:

您还可以通过以下方式在模板中使用它:

{{ current_project.thumbnail.url }}

回答by Eduard Gamonal

I found it annoying to write the same code for a serialized method field. If you have set correctly the MEDIA_ROOTto your S3 bucket URL, you can add a field to the serializer like:

我发现为序列化方法字段编写相同的代码很烦人。如果您已正确设置MEDIA_ROOTS3 存储桶 URL,则可以向序列化程序添加一个字段,例如:

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

    class Meta:
        model = Project

logo is an ImageField in the model. it must not be nullable in order to avoid errors like ValueError: The 'img' attribute has no file associated with it.

logo 是模型中的 ImageField。它不能为空,以避免出现类似的错误ValueError: The 'img' attribute has no file associated with it.

I only use .build_absolute_uriin a serializer methodfield to return absolute urls that use other views in my API. for example, in my project there is an URL /webviews/projects/<pk>that shows, a title and a button that collects some user input (i.e. not exactly what you would do with suffixes, as it's not a plain representation of the resource but includes some logic instead). the end point /projects/<pk>/contains a field "webview_url" ponting there, which is generated with SerializerMethodField. it's not media.

我只.build_absolute_uri在序列化程序方法字段中使用来返回在我的 API 中使用其他视图的绝对 url。例如,在我的项目中有一个/webviews/projects/<pk>显示的 URL 、一个标题和一个按钮,用于收集一些用户输入(即不完全是你会用后缀做什么,因为它不是资源的简单表示,而是包含一些逻辑)。终点/projects/<pk>/包含一个字段“webview_url”,它是用 SerializerMethodField 生成的。这不是媒体。

回答by jakobdo

Check you settings.py

检查你的settings.py

media settings.

媒体设置。

I had same error and found that:

我有同样的错误,发现:

MEDIA_URL = '/media/' did the trick.

MEDIA_URL = '/media/' 成功了。

Before i only had:

之前我只有:

MEDIA_URL = 'media/'

MEDIA_URL = '媒体/'

回答by Tarikul Islam Rasel

Just pass the context and pass request object. if you are using @api_view

只需传递上下文并传递请求对象。如果您使用@api_view

serializer = CustomerSerializer(customer, context={"request": request})

For ViewSet user get_serializer_context method

对于 ViewSet 用户 get_serializer_context 方法

class ProjectViewSet(viewsets.ModelViewSet):
queryset = Project.objects.all()
serializer_class = ProjectSerializer

def get_serializer_context(self):
    return {'request': self.request}

回答by Mohammad Jafar Mashhadi

No need for any overrides or customizations. DRF handles it automatically. Take a look at to_representationmethod of FileField:

无需任何覆盖或自定义。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

Note that it won't work if the context of the serializer is not set properly. If you're using ViewSets, no worries, everything is done silently but if you're instantiating the serializer manually you have to pass in the request in the context.

请注意,如果序列化程序的上下文设置不正确,它将无法工作。如果您使用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

https://www.django-rest-framework.org/community/3.0-announcement/#file-fields-as-urls

回答by bovenson

In my case, override to_representation method works right.

在我的情况下,覆盖 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"
    }
]

回答by Abhishek Palekar

Just pass this "context={'request': request}"argument where you call your model serializer class to serialize the object. You can follow the below snippet to get the complete URL field.

只需"context={'request': request}"在您调用模型序列化器类以序列化对象的地方传递此 参数。您可以按照以下代码段获取完整的 URL 字段。

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