在Django REST框架中发送不同的字段,而不是主键
序列化程序.py views.py 我像这样发送数据,它工作正常在Django REST框架中发送不同的字段,而不是主键,django,django-rest-framework,Django,Django Rest Framework,序列化程序.py views.py 我像这样发送数据,它工作正常 { "popularity": 83.0, "director": "Victor Fleming", "genre": [ 1, 2, 3, 4 ], "imdb_score": 8.3, "name": "The Wizard of Oz" } 但我想发送的数据有点像这样: { "popularity": 83.0, "direc
{
"popularity": 83.0,
"director": "Victor Fleming",
"genre": [
1,
2,
3,
4
],
"imdb_score": 8.3,
"name": "The Wizard of Oz"
}
但我想发送的数据有点像这样:
{
"popularity": 83.0,
"director": "Victor Fleming",
"genre": [
"Adventure",
"Family",
"Fantasy",
"Musical"
],
"imdb_score": 8.3,
"name": "The Wizard of Oz"
}
class MovieSerializer(serializers.ModelSerializer):
genre = serializer.ListField(child=serializers.CharField())
class Meta:
model = Movie
fields = [
'popularity',
'director',
'genre',
'imdb_score',
'name',
]
def validate(self, data):
genre = data.get('genre', [])
genre_obj_list = [Genre.objects.get(name=name) for name in genre.all()]
data.update({'genre': genre_obj_list})
return data
class MovieSerializer(serializers.ModelSerializer):
genre = serializers.StringRelatedField(many=True)
class Meta:
model = Movie
fields = [
'popularity',
'director',
'genre',
'imdb_score',
'name',
]
查看流派列表-我发送的不是主键而是name
这些是我的模型:
class Genre(models.Model):
name = models.CharField(max_length=30, unique=True) # make unique
def __str__(self):
return self.name
class Movie(models.Model):
popularity = models.FloatField(max_length=10)
director = models.CharField(max_length=30)
genre = models.ManyToManyField(Genre)
imdb_score = models.FloatField(max_length=10)
name = models.CharField(max_length=30)
即使在我列出数据或获取数据时,它也应该发送流派名称而不是id。我尝试使用StringRelatedField
,但它给了我错误
未实现错误位于/api/movie/
必须实现StringRelatedField.to_internal_value()
解决方案之一是覆盖序列化程序中的
类型
字段,以接受如下字符串列表:
{
"popularity": 83.0,
"director": "Victor Fleming",
"genre": [
"Adventure",
"Family",
"Fantasy",
"Musical"
],
"imdb_score": 8.3,
"name": "The Wizard of Oz"
}
class MovieSerializer(serializers.ModelSerializer):
genre = serializer.ListField(child=serializers.CharField())
class Meta:
model = Movie
fields = [
'popularity',
'director',
'genre',
'imdb_score',
'name',
]
def validate(self, data):
genre = data.get('genre', [])
genre_obj_list = [Genre.objects.get(name=name) for name in genre.all()]
data.update({'genre': genre_obj_list})
return data
class MovieSerializer(serializers.ModelSerializer):
genre = serializers.StringRelatedField(many=True)
class Meta:
model = Movie
fields = [
'popularity',
'director',
'genre',
'imdb_score',
'name',
]
在validate方法中,尝试根据每个对象的名称获取它们,并将它们放入一个新列表中,然后用新的对象列表更新数据结果。(我知道这不是最干净的解决方案,但效果很好)
您还可以尝试使用MethodSerializer
或定义一个GenreSerializer
,通过名称获取其中的对象,并在父序列化程序中将其用作输入。如果您始终将名称传递给序列化程序,则可以在模型定义中添加外键字段。你的情况
class Movie(models.Model):
popularity = models.FloatField(max_length=10)
director = models.CharField(max_length=30)
genre = models.ManyToManyField(Genre, db_column='name')
imdb_score = models.FloatField(max_length=10)
name = models.CharField(max_length=30)
重写序列化程序的create()
方法,如下所示
class MovieSerializer(serializers.ModelSerializer):
genre = serializers.ListSerializer(child=serializers.CharField())
class Meta:
model = Movie
fields = [
'popularity',
'director',
'genre',
'imdb_score',
'name',
]
def create(self, validated_data):
genre = validated_data.pop('genre',[])
movie = super().create(validated_data)
genre_qs = Genre.objects.filter(name__in=genre)
movie.genre.add(*genre_qs)
return movie
类MovieSerializer(serializers.ModelSerializer):
genre=serializers.ListSerializer(child=serializers.CharField())
类元:
模型=电影
字段=[
"人气",,
“董事”,
“体裁”,
“imdb_分数”,
“姓名”,
]
def创建(自我验证的_数据):
流派=已验证的_data.pop('流派',[])
movie=super().创建(已验证的\u数据)
genre\u qs=genre.objects.filter(name\u in=genre)
电影。流派。添加(*流派)
返回电影
假设您在MovieSerializer
中使用了StringRelatedField
,如下所示:
{
"popularity": 83.0,
"director": "Victor Fleming",
"genre": [
"Adventure",
"Family",
"Fantasy",
"Musical"
],
"imdb_score": 8.3,
"name": "The Wizard of Oz"
}
class MovieSerializer(serializers.ModelSerializer):
genre = serializer.ListField(child=serializers.CharField())
class Meta:
model = Movie
fields = [
'popularity',
'director',
'genre',
'imdb_score',
'name',
]
def validate(self, data):
genre = data.get('genre', [])
genre_obj_list = [Genre.objects.get(name=name) for name in genre.all()]
data.update({'genre': genre_obj_list})
return data
class MovieSerializer(serializers.ModelSerializer):
genre = serializers.StringRelatedField(many=True)
class Meta:
model = Movie
fields = [
'popularity',
'director',
'genre',
'imdb_score',
'name',
]
检索电影列表时,结果如下所示:
[
{
"popularity": 83.0,
"director": "Victor Fleming",
"genre": [
"Adventure",
"Family",
"Fantasy",
"Musical"
],
"imdb_score": 8.3,
"name": "The Wizard of Oz"
}
]
但是如果您想创建一个新电影,那么它将无法工作,因为StringRelatedField
是只读的
但是,您可以创建自定义相关字段
这是完整的序列化程序.py
:
from rest_framework import serializers
from .models import Genre, Movie
class GenreRelatedField(serializers.RelatedField):
def display_value(self, instance):
return instance
def to_representation(self, value):
return str(value)
def to_internal_value(self, data):
return Genre.objects.get(name=data)
class MovieSerializer(serializers.ModelSerializer):
genre = GenreRelatedField(
queryset=Genre.objects.all(),
many=True
)
class Meta:
model = Movie
fields = (
'popularity',
'director',
'genre',
'imdb_score',
'name',
)
这是一个简单的示例,可以通过多种方式进行高度定制
方法display\u value
定义对象类型的显示方式,例如在表单中。在这里,它只返回对象类型,即\uuu str\uuu
的输出
方法to_representation
定义了对象类型在输出中的显示方式(JSON或XML)。它与前面的方法非常相似,但这里我们必须显式地将流派转换为字符串。当然,您可以根据需要创建更复杂的输出
方法to_internal_value
通过获取给定值的对象类型来解决实际问题。如果您有一个更复杂的方法来表示
,那么您需要扩展逻辑来获得适当的对象
使用这种方法,您可以以所需的形式发布JSON,指定流派名称而不是它们的ID
我希望这个例子也能帮助其他人。简单的解决方案是将流派
模型更改为使用名称
作为主键,如下所示:
类类型(models.Model):
name=models.CharField(最大长度=30,主键=True)
这并不重要,但这也将在数据库中保存一列,因为自动生成的id
列将消失:)
更新
在对这个答案的评论中进行了一些讨论之后,我发现重要的一点是要提到,使用任何类型作为主键,之后也应该避免更改该字段
这是因为对主键的更改还需要更新指向该主键的所有外键,并且(就这个问题而言)即使您的带有类型的表可能相对较小,你可能有大量的电影指向每种类型。请展示你的GenreSerializer
@cezar没有`GenreSerializer``你的genrename
是唯一的吗?是的,它是唯一的@shakil你在MovieSerializer
中使用过StringRelatedField
吗:name=serializers.StringRelatedField(many=True)
在类元之前
?它在/api/movie/'ManyRelatedManager'对象处给出错误-TypeError不可编辑
@Huzaifsayed尝试[color.objects.get(name=name)获取流派中的名称。all()
不应验证()
在某个时候验证其他字段?您的回答几乎是正确的。请参阅已接受的答案。我已解决问题。问题是我现在添加到答案中的genre.all()
。我想这实际上是一个有效的答案。相对解释。这是非常令人失望的,有人试图把一个价值,即使这是不相关或不适当的,你认为你应该总是把评论,然后你可以投反对票。不加评论的否决票非常令人失望。我支持你@Shakil@HuzaifSayyed你不应该投这个票。如果你认为我的答案是增值,那就这么做吧。谢谢你的支持。我觉得你的回答很有帮助,这就是为什么我投你的票。可能不是答案是的,这会起作用,但使用字符串作为主键是一种非常糟糕的做法。是的,我同意@cezar。但我不想我的结构被破坏destroy@cezar为什么这是一种“非常糟糕的做法”?有时,方便性和易用性胜过其他一切。在这种情况下,我觉得更重要的是:你打算增加多少种风格?当然不是几百万,甚至不是几千,而且你已经将字段限制为30个字符,这比