如何在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关联模型的设计:
- 更新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), ]
- 更新序列化器:
直接用嵌套序列化器,不用手动改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实例:
- 修改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
相关产品推荐
相关产品推荐

