DRF中FileField在GenericView与APIView的路径差异及自定义方案
Django文件上传:ListAPIView与APIView返回路径差异原因及自定义方案
问题复现
定义的Model和Serializer
import os import uuid from django.db import models from rest_framework import serializers def save_file(instance, filename): _, ext = os.path.splitext(filename) return f"{instance.id}{ext}" class FileModel(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) title = models.CharField(max_length=64) file = models.FileField(upload_to=save_file) created = models.DateTimeField(auto_now_add=True) class FileModelSerializer(serializers.ModelSerializer): class Meta: model = FileModel fields = "__all__"
不同视图的返回差异
- 使用
generics.ListAPIView时,返回绝对路径:
[ { "id": "2c5488ec-7b5d-45ab-9c43-058c19d76626", "title": "file.png", "file": "http://127.0.0.1:8000/media/2c5488ec-7b5d-45ab-9c43-058c19d76626.png", "created": "2024-02-07T02:29:29.845808Z" } ]
- 使用自定义
APIView时,返回相对路径:
from rest_framework.views import APIView from rest_framework.response import Response class FileListApi(APIView): def get(self, request): serializer = FileModelSerializer(FileModel.objects.all(), many=True) return Response(serializer.data)
[ { "id": "2c5488ec-7b5d-45ab-9c43-058c19d76626", "title": "file.png", "file": "/media/2c5488ec-7b5d-45ab-9c43-058c19d76626.png", "created": "2024-02-07T02:29:29.845808Z" } ]
差异原因
核心在于序列化器是否能获取到request对象:
generics.ListAPIView继承自GenericAPIView,框架会自动将当前请求的request对象传入序列化器的context参数中。DRF的FileField序列化逻辑中,若存在request,则会基于请求的scheme(http/https)和host生成完整的绝对URL。- 手动实例化
FileModelSerializer时(如APIView中的写法),未传入context={'request': request},序列化器无法获取请求上下文,因此仅返回文件的相对存储路径。
自定义路径输出(需求:统一返回相对路径)
方案1:重写序列化器的FileField
自定义一个返回相对路径的FileField,替换默认字段:
class RelativeFileField(serializers.FileField): def to_representation(self, value): if value and hasattr(value, 'name'): return value.name return None class FileModelSerializer(serializers.ModelSerializer): file = RelativeFileField() class Meta: model = FileModel fields = "__all__"
方案2:使用SerializerMethodField
通过自定义方法控制返回内容:
class FileModelSerializer(serializers.ModelSerializer): file = serializers.SerializerMethodField() def get_file(self, obj): # 返回相对路径,即文件在media下的存储路径 return obj.file.name if obj.file else None class Meta: model = FileModel fields = "__all__"
两种方案都能让ListAPIView和APIView统一返回相对路径,无需修改视图逻辑。
内容的提问来源于stack exchange,提问作者lustfully
相关产品推荐
相关产品推荐

