web-dev-qa-db-ja.com

Django REST Framework and FileField absolute url

次のモデルを含む単純な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でカスタムフィールドを作成する必要があると思いますが、成功していません。

23
Mark Semsel

試す 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を作成できるようにします。 1つの方法は、次のように、シリアライザーの作成時に明示的に渡すことです。

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

ありがとう、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') 
7
Mark Semsel

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
Johnny Well

シリアル化されたメソッドフィールドに同じコードを書くのは面倒です。 _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._のようなエラーを回避するために、null可能であってはなりません

APIの他のビューを使用する絶対URLを返すために、シリアライザーメソッドフィールドで_.build_absolute_uri_のみを使用します。たとえば、私のプロジェクトでは、URL _/webviews/projects/<pk>_が表示され、タイトルとユーザー入力を収集するボタンがあります(つまり、リソースの単なる表現ではなく、代わりにいくつかのロジック)。エンドポイント_/projects/<pk>/_には、「webview_url」フィールドが含まれ、SerializerMethodFieldで生成されます。メディアではありません。

3
Eduard Gamonal

オーバーライドやカスタマイズの必要はありません。 DRFが自動的に処理します。を見てみましょう to_representationFileFieldのメソッド:

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

シリアライザーのコンテキストが適切に設定されていない場合は機能しないことに注意してください。 ViewSetsを使用している場合、心配はありませんが、すべてが静かに行われますが、シリアライザーを手動でインスタンス化する場合は、コンテキストでリクエストを渡す必要があります。

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

3
MJafar Mash

コンテキストを渡し、リクエストオブジェクトを渡すだけです。 @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

Settings.pyを確認してください

メディア設定。

私は同じエラーがあり、それを見つけました:

MEDIA_URL = '/ media /'はトリックを行いました。

私が持っていた前に:

MEDIA_URL = 'media /'

1
jakobdo

私の場合、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
bovenson