【问题标题】:unique_together of two field messes with read_only_fields两个字段的 unique_together 与 read_only_fields 混淆
【发布时间】:2016-03-30 19:09:48
【问题描述】:

我有这个代码用于评价课程,用户和课程应该从请求授权和 URL 自动添加:

#views.py

class RatingViewSet(
    mixins.ListModelMixin,
    mixins.CreateModelMixin,
    viewsets.GenericViewSet
):
    permission_classes = [permissions.IsAuthenticated]
    serializer_class = RatingSerializer

    def perform_create(self, serializer):
        lessonInstance = Lesson.objects.get(id = self.kwargs['lessonID'])
        serializer.save(user=self.request.user, lesson = lessonInstance)
    def get_queryset(self):
        lessonID = self.kwargs['lessonID']
        return Rating.objects.filter(user=self.request.user, lesson=lessonID)

#serializers.py

class RatingSerializer(serializers.ModelSerializer):
    class Meta:
        model = Rating
        fields = ('id', 'lesson','user', 'difficulty')
        read_only_fields = ('id', 'user','lesson')

#models.py

class Rating(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    lesson = models.ForeignKey('lessons.Lesson')
    difficulty = models.IntegerField()
    class meta:
        unique_together('user','lesson')

我希望每个用户/课程最多获得 1 个评分,因此 unique_together('user','lesson')。但是有一个问题:只要该约束在代码中,没有userlesson 字段的请求就会被拒绝并出现field required 错误,即使它们是read_only

(如果我使用unique_together('user','lesson') 迁移,则删除该行它可以工作,但一旦出现我就会收到错误。)

我想保留那段代码,以免在以后的迁移中意外删除unique_together 约束。

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    这是一种特殊情况,需要采用不同的方法。以下是django-rest-frameworkdocumentation(见注)关于这个案例的说法:

    处理这个问题的正确方法是明确指定字段 序列化程序,提供read_only=Truedefault=… 关键字参数。

    在您的情况下,您需要在 RatingSerializer 上明确定义 userlesson 字段,如下所示:

    class RatingSerializer(serializers.ModelSerializer):
        user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())  # gets the user from request
        lesson = serializers.PrimaryKeyRelatedField(read_only=True, default=None)  # or any other appropriate value
    
        class Meta:
            model = Rating
            fields = ('id', 'lesson','user', 'difficulty')
    

    祝你好运!

    【讨论】:

      【解决方案2】:

      如果一个字段是read_only=True,那么validated_data 将忽略它的数据=> 原因error required field,阅读更多doc

      我也在类似的情况下遇到了这个问题,然后尝试了上面@iulian 的答案,但没有运气! 不再支持此组合 read_only + default 行为,请检查 this

      我通过 2 个解决方案解决了这个问题:

      • 我的模特:
      class Friendship(TimeStampedModel):
          """Model present Friendship request"""
          from_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='friendship_from_user')
          to_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='friendship_to_user')
      
          class Meta:
              unique_together = ('from_user', 'to_user')
      

      解决方案1.编写自己的CurrentUserDefault类,获取用户id,然后设置为序列化器的默认属性数据(Ref from #51940976

      class CurrentUserDefault(object):
          def set_context(self, serializer_field):
              self.user_id = serializer_field.context['request'].user.id
      
          def __call__(self):
              return self.user_id
      
      class FriendshipSerializer(serializers.ModelSerializer):
          from_user_id = serializers.HiddenField(default=CurrentUserDefault())
      
          class Meta:
              model = Friendship
              fields = ('id', 'from_user', 'from_user_id', 'to_user', 'status')
              extra_kwargs = {
                  'from_user': {'read_only': True},
              }
      

      解决方案2. 重写序列化器的create方法为用户id设置数据(Ref from this

      class FriendshipSerializer(serializers.ModelSerializer):
          class Meta:
              model = Friendship
              fields = ('id', 'from_user', 'to_user', 'status')
              extra_kwargs = {
                  'from_user': {'read_only': True},
              }
      
      
          def create(self, validated_data):
              """Override create to provide a user via request.user by default.
      
              This is require since the read_only `user` filed is not included by default anymore since
              https://github.com/encode/django-rest-framework/pull/5886.
              """
              if 'user' not in validated_data:
                  validated_data['from_user'] = self.context['request'].user
      
              return super(FriendshipSerializer, self).create(validated_data)
      

      我希望这会有所帮助!

      【讨论】:

        猜你喜欢
        • 2011-11-09
        • 1970-01-01
        • 2021-02-14
        • 1970-01-01
        • 2014-07-09
        • 1970-01-01
        • 2011-09-21
        • 2012-05-22
        • 2017-07-01
        相关资源
        最近更新 更多