You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django Rest Framework中正确序列化关联的Publisher字段

问题描述

我有两个Django模型:

  • Book(包含Wagtail StreamField):
class Book(models.Model):
    title = models.CharField(max_length=255)
    cover_image = StreamField(...)
    ...
  • Publisher(标准Django模型):
class Publisher(models.Model):
    name = models.CharField(max_length=255)
    ...

为了在Wagtail后台用PublisherChooser选出版社,我给Book加了字段并配置了后台面板:

class Book(models.Model):
    title = models.CharField(max_length=255)
    cover_image = StreamField(...)
    ...
    publisher = models.CharField(max_length=10)

    panels = [
        FieldPanel("title"),
        FieldPanel("cover_image"),
        FieldPanel("publisher", widget=PublisherChooser),
    ]

现在后台创建Book时PublisherChooser能正常用,但序列化Book模型出问题:我写了PublisherSerializer和BookSerializer,其中BookSerializer用了PrimaryKeyRelatedField还改写了to_representation方法,结果返回的publisher字段数据异常:

"publisher": {
            "name": "<django.db.models.query_utils.DeferredAttribute object at 0x7f9ffa146310>",
}

需要解决这个序列化异常,输出正确的关联数据。

问题根源

问题出在Book模型的publisher字段是CharField类型,不是ForeignKey,但序列化时却按关联模型的逻辑处理,导致没拿到实际的Publisher实例,反而直接引用了模型的字段属性(DeferredAttribute是Django模型字段的内部类,代表未实例化的字段)。

解决方案

方案1:把Book的publisher字段改成ForeignKey(推荐)

这是最规范的做法,符合Django关联模型的设计:

  1. 更新Book模型:
from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=255)
    cover_image = StreamField(...)
    # 替换原来的CharField为ForeignKey
    publisher = models.ForeignKey(
        Publisher,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="books"
    )

    panels = [
        FieldPanel("title"),
        FieldPanel("cover_image"),
        # Wagtail的ChooserPanel支持ForeignKey,自定义的PublisherChooser也能继续用
        FieldPanel("publisher", widget=PublisherChooser),
    ]
  1. 更新序列化器:
    直接用嵌套序列化器,不用手动改to_representation:
from rest_framework import serializers
from .models import Book, Publisher

class PublisherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publisher
        fields = ["id", "name"]

class BookSerializer(serializers.ModelSerializer):
    # 直接嵌套PublisherSerializer
    publisher = PublisherSerializer(read_only=True)

    class Meta:
        model = Book
        fields = ["title", "cover_image", "publisher"]

这样序列化后就能正确输出publisher的完整数据,不会再出现DeferredAttribute的错误。

方案2:不修改Book模型字段类型(兼容现有数据)

如果因为已有数据不能改字段类型,就在序列化时手动通过ID查Publisher实例:

  1. 修改BookSerializer的to_representation方法:
    确保先从publisher字段存的ID查到实际的Publisher实例,再序列化:
from rest_framework import serializers
from .models import Book, Publisher

class PublisherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publisher
        fields = ["id", "name"]

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ["title", "cover_image", "publisher"]

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        # 拿到publisher字段存的ID字符串
        publisher_id = representation.get("publisher")
        if publisher_id:
            try:
                # 查询对应的Publisher实例
                publisher = Publisher.objects.get(id=publisher_id)
                # 用PublisherSerializer序列化后替换原字段值
                representation["publisher"] = PublisherSerializer(publisher).data
            except Publisher.DoesNotExist:
                # 处理ID不存在的情况
                representation["publisher"] = {"name": "未知出版社"}
        else:
            representation["publisher"] = None
        return representation

注意:这种方案要保证CharField存的publisher值是有效的Publisher ID字符串,不然会查询失败,建议保留异常处理逻辑。

内容的提问来源于stack exchange,提问作者Faith Baghan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 08:01:25