【问题标题】:Django Rest Framework SerializerMethodField only on GET RequestDjango Rest Framework SerializerMethodField 仅在 GET 请求上
【发布时间】:2019-01-07 03:19:51
【问题描述】:

我的 DRF 后端在这里遇到了一点问题。

我正在使用某些模型的选项填充字段。

我对一个模型有外键要求。当我创建模型时,我想将它保存在外国 id 下。

当我请求模型时,我希望模型具有选择字段映射到的任何内容。

我可以使用 SerializerMethodField 执行此操作,但是当我尝试创建模型时,我收到 400 错误,因为该块无效。如果我删除 SerializerMethodField,我可以保存,但从请求中获取存储在数据库中的数字。

任何帮助将不胜感激。

class BlockViewSet(ModelViewSet):
    model = apps.get_model('backend', 'Block')
    queryset = model.objects.all()
    serializer_class = serializers.BlockSerializer
    permissions = ('All',)

    def create(self, request, format=None):
        data = request.data 
        data['user'] = request.user.id
        data['goal'] = WorkoutGoal.objects.get(goal=data['goal']).id
        block = serializers.BlockSerializer(data=data, context={'request': request})
        if block.is_valid():
            new_block = block.save()
            return Response({'block': {'name': new_block.name, 'id': new_block.id}}, status=status.HTTP_201_CREATED)
        else:
            return Response(block.errors, status=status.HTTP_400_BAD_REQUEST)

class WorkoutGoalSerializer(serializers.ModelSerializer):
    class Meta:
        model = apps.get_model('backend', 'WorkoutGoal')
        fields = ('goal',)

    goal = serializers.SerializerMethodField(read_only=True, source='get_goal')

    def get_goal(self, obj):
        return dict(WorkoutGoal.GOALS).get(obj.goal)

class BlockSerializer(serializers.ModelSerializer):
    workout_count = serializers.IntegerField(required=False)
    completed_workouts = serializers.IntegerField(required=False)
    goal = WorkoutGoalSerializer()

    class Meta:
        model = apps.get_model('backend', 'Block')
        read_only_fields = ('workout_count', 'completed_workouts')
        fields = read_only_fields + ('id', 'name', 'user', 'created', 'goal')

上面的代码返回了正确的选择,但是我不能保存在它下面。删除 goal = WorkoutGoalSerializer() 并保存但不返回映射的选项。

【问题讨论】:

  • 我没有玩过这个,但是为什么goal不在BlockSerializerread_only_fields中?
  • Block 模型上的外键字段是必需的。只读不是针对您未写入数据库的内容吗?
  • 我明白了。我在第一次阅读时误解了。
  • 我没试过,但这看起来像 this question 的副本...特别是 this answer 看起来应该可以解决问题。

标签: django django-rest-framework


【解决方案1】:

我认为这会像一个魅力,

class WorkoutGoalSerializer(serializers.ModelSerializer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if 'request' in self.context and self.context['request'].method == 'GET':
            self.fields['goal'] = serializers.SerializerMethodField(read_only=True, source='get_goal')

    class Meta:
        model = apps.get_model('backend', 'WorkoutGoal')
        fields = ('goal',)

    goal = serializers.SerializerMethodField(read_only=True, source='get_goal')  # remove this line

    def get_goal(self, obj):
        return dict(WorkoutGoal.GOALS).get(obj.goal)

这是如何工作的?
如果重新调用的方法是 GET,它将使用 SerializerMethodField 重新启动 goal 字段。

记住一件事,你应该删除这条线

goal = serializers.SerializerMethodField(read_only=True, source='get_goal')

【讨论】:

  • 做了一些改变,但这种模式解决了我的问题。谢谢
【解决方案2】:

序列化器.py

class BlockCreateSerializer(serializers.ModelSerializer):
    workout_count = serializers.IntegerField(required=False)
    completed_workouts = serializers.IntegerField(required=False)

    class Meta:
        model = apps.get_model('backend', 'Block')
        read_only_fields = ('workout_count', 'completed_workouts')
        fields = read_only_fields + ('id', 'name', 'user', 'created', 'goal')

class BlockSerializer(serializers.ModelSerializer):
    workout_count = serializers.IntegerField(required=False)
    completed_workouts = serializers.IntegerField(required=False)
    goal = WorkoutGoalSerializer()

    class Meta:
        model = apps.get_model('backend', 'Block')
        read_only_fields = ('workout_count', 'completed_workouts')
        fields = read_only_fields + ('id', 'name', 'user', 'created', 'goal')

views.py

class BlockViewSet(ModelViewSet):
    model = apps.get_model('backend', 'Block')
    queryset = model.objects.all()
    serializer_class = serializers.BlockSerializer
    permissions = ('All',)

    def get_serializer_class(self):
        if self.action == 'create':
            return serializers.BlockCreateSerializer
        else:
            return self.serializer_class

    def create(self, request, format=None):
        data = request.data 
        data['user'] = request.user.id
        data['goal'] = WorkoutGoal.objects.get(goal=data['goal']).id
        block = self.get_serializer(data=data)
        if block.is_valid():
            new_block = block.save()
            return Response({'block': {'name': new_block.name, 'id': new_block.id}}, status=status.HTTP_201_CREATED)
        else:
            return Response(block.errors, status=status.HTTP_400_BAD_REQUEST)

覆盖get_serializer_classcreate 和其他操作返回不同的serializer_class (list\retrieve\update\partial_update)

【讨论】:

    猜你喜欢
    • 2019-05-12
    • 2020-08-26
    • 1970-01-01
    • 2016-07-21
    • 2016-09-25
    • 2020-11-09
    • 2020-04-23
    • 1970-01-01
    • 2019-05-04
    相关资源
    最近更新 更多