【问题标题】:Incomplete media url in nested serializers嵌套序列化程序中的媒体 url 不完整
【发布时间】:2026-01-13 05:35:01
【问题描述】:

我有一个 django-rest-api 应用程序,其中包含模型 Record、Tag 和 Weight,其中 Record 和 Tag 通过 Weight 模型具有多对多关系:

# models.py
class Tag(models.Model):
    image = models.ImageField(upload_to='tag_images', null=True, blank=True)

class Record(models.Model):
    file = models.FileField(upload_to='record_files', null=True, blank=True)

class Weight(models.Model):
    record = models.ForeignKey(
        Record,
        related_name='weights',
        on_delete=models.CASCADE
    )
    tag = models.ForeignKey(
        Tag,
        related_name='weights',
        on_delete=models.CASCADE
    )
    value = models.IntegerField()

    class Meta:
        unique_together = ('record', 'tag',)

Record 和 Tag 模型都有一个 FileField/ImageField 参数。在我的 REST API 视图中,我想显示记录详细信息及其完整文件 url 和所有相关标签及其完整图像 url。这就是我的序列化程序的样子:

# serializers.py
class RecordSerializer(serializers.ModelSerializer):
    tags = serializers.SerializerMethodField()

    class Meta:
        model = Record
        fields = ('id', 'file', 'tags')

    def get_tags(self, obj):
        return TagSerializer(Tag.objects.filter(weights__in=obj.weights.all()), many=True).data

class TagSerializer(serializers.ModelSerializer):

    class Meta:
        model = Tag
        fields = ('id', 'image')

问题是,当我看到记录详细信息视图时,它的文件 url 是完整的,但标签图像 url 不是:

# record detail results
{
    "id": 1,
    "file": "http://127.0.0.1:8080/media/record_files/record00.mp3",
    "tags": [
        {
            "id": 4,
            "image": "/media/tag_images/image04.jpg"
        },
        {
            "id": 10,
            "image": "/media/tag_images/image10.jpg"
        }
    ]
}

最后我通过创建一个单独的标签列表视图绕过了这个问题,现在即使我的 serializers.py 中的 a 没有改变一点,url 也是完整的。

# record tag list results
[
    {
        "id": 4,
        "image": "http://127.0.0.1:8080/media/tag_images/image04.jpg"
    },
    {
        "id": 10,
        "image": "http://127.0.0.1:8080/media/tag_images/image10.jpg"
    }
]

无论如何,我想这是一个更干净的 rest api 解决方案(尽管我很确定我不需要单独记录详细信息及其标签)。尽管如此,当数据是嵌套序列化程序的一部分时,为什么 url 不完整仍然让我感到困扰。这是它的本意还是我做错了什么?

【问题讨论】:

    标签: django django-rest-framework django-serializer django-media


    【解决方案1】:

    在您的视图中像这样初始化您的序列化程序:

    serialized_store = StoreSerializer(store, context={"request": request})

    source

    【讨论】:

      【解决方案2】:

      似乎您可能找到了更好的解决方案,但以防万一您仍然好奇:您最初尝试的问题是您将“嵌套”序列化程序调用放在 SerializerMethodField 中。声明nested serializer 的常用方法是在设置实际字段时初始化序列化程序。在您的情况下,它看起来像这样:

      class RecordSerializer(serializers.ModelSerializer):
          tags = TagSerializer()
      

      当您这样做时,TagSerializer 可以从父序列化程序推断上下文,并且该上下文的一部分是请求。然后在to_representation 调用build_absolute_uri 中使用它来生成您希望的完整网址。

      【讨论】:

      • 非常感谢您的帮助。我已经阅读了声明嵌套序列化程序的常用方法。尽管如此,标签模型并没有直接通过外键连接到记录模型(它们通过权重模型相互关联)所以我得到“'记录'对象没有属性'标签'”。错误,除了 SerializerMethodField 之外,我还没有找到传递所需数据的方法。除此之外,我不确定如何访问除 SerializerMethodField 之外的当前记录(在我的示例中保存在 obj 变量中的对象)。有可能吗?
      • 对不起,我看错了。当您说“通过权重模型”时,我假设您与 through modelManyToMany 关系(权重模型看起来像)。这是一种选择。
      • 如果您绝对不想在数据库中建立直接关系,那么我知道处理它的唯一其他“标准”方式将是双重嵌套(包括嵌套一个 WeightSerializer TagSerializer)。另一个 hacky 选项是生成一个假的 request 并将其传递给 get_tags 中的 TagSerializer,但这需要更多的工作。
      【解决方案3】:

      你需要覆盖方法:

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

      【讨论】:

      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center