使用 ChoiceField 时,类型错误对象不是 JSON 可序列化的

Posted

技术标签:

【中文标题】使用 ChoiceField 时,类型错误对象不是 JSON 可序列化的【英文标题】:Type error object is not JSON serializable when using ChoiceField 【发布时间】:2016-07-07 00:28:26 【问题描述】:

我正在使用 Django Rest 框架在 Django 中创建一个 REST API,并且不能使用任何其他库或插件。过去几天我一直遇到一个问题,我无法解决。

在我的 seralizer.py 中,我有以下内容

class BookSerializer(serializers.HyperlinkedModelSerializer):
    owner = serializers.ReadOnlyField(source = 'owner.username')
    genres = serializers.ChoiceField(choices=Genre.objects.values_list())
    # genres = GenreSerializer()

class Meta:
    model = VideoGame
    fields = ('title', 'description', 'brief', 'genres', 'owner') 

我希望这样当用户访问 GUI 并尝试制作一本新书时,他们可以从选择字段中选择一个流派,然后将其转换为它的 ID。如果用户通过 REST API 访问,他们还必须输入 ID。

当数据库为空白时,我能够成功地将一本新书发布到服务器并使用选择字段选择下拉列表。但是当数据库有一个条目时,我得到下面的错误。当我用序列化程序替换选择字段时,这个问题就解决了。但是,我留下了一个输入字段,而不是一个选择字段。有谁知道我该如何解决这个问题?

TypeError at /book/
<Genre: Genre object> is not JSON serializable

【问题讨论】:

【参考方案1】:

您需要查看 a) ChoiceField 期望收到什么作为选择,b) values_list() 返回什么。

a) 来自the docs 的ChoiceField 需要“有效值列表或(key, display_name) 元组列表。” 在这种情况下,有效值将是主键ID你的流派。

b) 根据the docs values_list() 不带参数返回“模型中的所有字段,按照它们被声明的顺序。”

我认为您不会通过使用choices=Genre.objects.values_list() 得到问题中显示的错误消息...也许您尝试了choices=Genre.objects.all() 代替?

无论如何,你可以这样做:

class BookSerializer(serializers.HyperlinkedModelSerializer):
    owner = serializers.ReadOnlyField(source = 'owner.username')
    genres = serializers.ChoiceField(choices=Genre.objects.values_list('pk', flat=True))

如果您的 Genre 模型上有一个字段可以成为一个好的“显示名称”,那么您可以这样做:

class BookSerializer(serializers.HyperlinkedModelSerializer):
    owner = serializers.ReadOnlyField(source = 'owner.username')
    genres = serializers.ChoiceField(choices=Genre.objects.values_list('pk', 'name'))

但是上述两个选项都有一个问题,那就是查询集将在您导入包含资源的文件后立即进行评估(因为它立即由 ChoiceField 转换为列表)。

在导入时有这样的副作用是不好的做法。此外,由于每次启动服务器时它只评估一次,因此当您更改流派时,这些选择将过时。

幸好有更好的选择:http://www.django-rest-framework.org/api-guide/relations/#primarykeyrelatedfield

这类似于 Django 中的 ModelChoiceField。它有一个 queryset 参数,用于验证选择......但与 ChoiceField 不同,它知道不立即评估查询集。每次需要验证选择时都会对其进行评估。

所以你应该这样做:

class BookSerializer(serializers.HyperlinkedModelSerializer):
    owner = serializers.ReadOnlyField(source = 'owner.username')
    genres = serializers.PrimaryKeyRelatedField(queryset=Genre.objects.all())

如果您需要为您在关系字段中的选择自定义“显示名称”,您可以查看the docs here。

【讨论】:

我想使用 ChoiceField 因为它会自动在 OPTIONS 调用中添加可用选项。我用 PrimaryKeyRelatedField 尝试了这种方式,但它没有返回 OPTIONS 中的可用选项,并且使用 ChoiceField 我遇到了与 @Oonah 相同的问题我的代码:activity_type = serializers.ChoiceField( choices=ActivityType.objects.filter( primary_activity=True).values_list("id", "name") )【参考方案2】:

当模型已更改但未迁移时,我通常会收到此类错误。 尝试这样做。这可能会有所帮助

【讨论】:

以上是关于使用 ChoiceField 时,类型错误对象不是 JSON 可序列化的的主要内容,如果未能解决你的问题,请参考以下文章

使用元组时,ChoiceField 不显示空标签

添加 SMS 插件 Ionic 时类型错误对象不是函数

Django MultiValueField - 如何将选择传递给 ChoiceField?

类型错误:使用烧瓶读取 pdf 文件时预期的 str、字节或 os.PathLike 对象,而不是 FileStorage

基于选择的 Django 模板/ChoiceField 显示字段

调用 Flash restful 服务时在烧瓶上显示错误。 TypeError:“响应”类型的对象不是 JSON 可序列化的