在Django REST Framework中代替主键发送不同的字段

Huz*_*yed 5 django django-rest-framework

serializers.py

class MovieSerializer(serializers.ModelSerializer):

    class Meta:
        model = Movie
        fields = [
            'popularity',
            'director',
            'genre',
            'imdb_score',
            'name',
        ]  
Run Code Online (Sandbox Code Playgroud)

views.py

class MovieList(generics.ListCreateAPIView):
    queryset = Movie.objects.all().order_by('-id')[:10]
    serializer_class = MovieSerializer
    permission_classes = (IsAuthenticated,)

    def list(self, request):
        queryset = self.get_queryset()
        serializer = MovieSerializer(queryset, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        data = request.data
        if isinstance(data, list):
            serializer = self.get_serializer(data=request.data, many=True)
        else:
            serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Run Code Online (Sandbox Code Playgroud)

我这样发送数据,它可以正常工作。

{
   "popularity": 83.0,
   "director": "Victor Fleming",
   "genre": [
      1,
      2,
      3,
      4
   ],
   "imdb_score": 8.3,
   "name": "The Wizard of Oz"
}
Run Code Online (Sandbox Code Playgroud)

但是我想像这样发送数据:

{
   "popularity": 83.0,
   "director": "Victor Fleming",
   "genre": [
      "Adventure",
      "Family",
      "Fantasy",
      "Musical"
   ],
   "imdb_score": 8.3,
   "name": "The Wizard of Oz"
}
Run Code Online (Sandbox Code Playgroud)

请参阅流派列表-我要发送的不是主键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)
Run Code Online (Sandbox Code Playgroud)

即使我列出数据或获取数据,它也应该发送流派名称而不是ID。我尝试使用StringRelatedField,但这给了我错误。

必须实现/ api / movie / StringRelatedField.to_internal_value()处的NotImplementedError。

cez*_*zar 12

假设你StringRelatedField在你MovieSerializer这样使用:

class MovieSerializer(serializers.ModelSerializer):
    genre = serializers.StringRelatedField(many=True)

    class Meta:
        model = Movie
        fields = [
            'popularity',
            'director',
            'genre',
            'imdb_score',
            'name',
        ]
Run Code Online (Sandbox Code Playgroud)

检索电影列表时,结果如下所示:

[
    {
        "popularity": 83.0,
        "director": "Victor Fleming",
        "genre": [
             "Adventure",
             "Family",
             "Fantasy",
             "Musical"
        ],
        "imdb_score": 8.3,
        "name": "The Wizard of Oz"
    }
]
Run Code Online (Sandbox Code Playgroud)

但是如果你想创建一部新电影,那么它就行不通了,因为它StringRelatedField是只读的。

但是,您可以创建自定义相关字段。

这是完整的serializers.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',
        )   
Run Code Online (Sandbox Code Playgroud)

这是一个可以通过多种方式高度定制的简单示例。

该方法display_value定义了对象流派的显示方式,例如在表单中。这里它只返回对象 Genre ,即__str__.

该方法to_representation定义了对象 Genre 在输出(JSON 或 XML)中的显示方式。它与前面的方法非常相似,但在这里我们必须明确地将 Genre 转换为字符串。当然,您可以根据您的要求创建更复杂的输出。

该方法to_internal_value通过获取给定值的对象类型来解决您的实际问题。如果这里有更复杂的方法to_representation,则需要扩展逻辑来获取适当的对象。

使用这种方法,您可以以您想要的形式发布 JSON,指定流派名称而不是它们的 id。

我希望这个例子也能帮助其他人。

  • 我喜欢这个答案,因为它很容易扩展,并且允许比其他答案更复杂的解决方案。 (3认同)

JPG*_*JPG 6

如下重写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
Run Code Online (Sandbox Code Playgroud)