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

我使用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.如果用户通过其余API访问,则他们还必须输入ID.

当数据库为空时,我能够将新书成功发布到服务器并使用choicefield选择下拉列表.但是当数据库有一个条目时,我会得到以下错误.当我用序列化器替换choicefield时,这就解决了.然而,我留下了输入字段而不是选择字段.有谁知道我怎么解决这个问题?

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

最佳答案 您需要查看a)ChoiceField期望接收什么作为选择,以及b)values_list()返回什么.

a)从the docs开始,ChoiceField需要“有效值列表或(key,display_name)元组列表.”在这种情况下,有效值将是您的类型的主键ID.

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

我不认为您可以通过使用choices = Genre.objects.values_list()获得您在问题中显示的错误消息…也许您尝试选择= Genre.objects.all()而不是?

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

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

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

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.

点赞