Django REST框架:注解后对象没有属性;尝试在序列化器上获取字段 <field> 的值时出现 AttributeError

问题描述 投票:0回答:1

我有一个自定义用户模型和订阅模型,其中包含

ForeignKey
给订阅者和要订阅的用户。

class Subscription(models.Model):
    user = models.ForeignKey(
        ApiUser,
        on_delete=models.CASCADE,
        related_name='subscriber'
    )
    subscription = models.ForeignKey(
        ApiUser,
        on_delete=models.CASCADE,
        related_name='subscriptions'
    )

我还有用于订阅的视图集和两个序列化器:用于写入和读取。

class SubscribeViewSet(mixins.ListModelMixin,
                       mixins.CreateModelMixin,
                       mixins.DestroyModelMixin,
                       viewsets.GenericViewSet):
    queryset = ApiUser.objects.all()
    serializer_class = SubscriptionSerializerForRead
    permission_classes = (permissions.AllowAny,)

    def get_queryset(self):
        queryset = ApiUser.objects.all().annotate(
            is_subscribed=Case(
                When(
                    subscribtions__exact=self.request.user.id,
                    then=Value(True)
                ),
                default=Value(False),
                output_field=BooleanField()
            )
        ).order_by('id')
        return queryset

    def get_serializer_context(self):
        context = super().get_serializer_context()
        context.update({'subscription_id': self.kwargs['pk']})
        return context
    
    def get_serializer_class(self):
        if self.request.method not in permissions.SAFE_METHODS:
            return SubscriptionSerializerForWrite
        return SubscriptionSerializerForRead

    @action(
        methods=['POST'],
        detail=True,
        url_path='subscribe'
    )
    def subscribe(self, request, pk):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
class SubscriptionSerializerForRead(serializers.ModelSerializer):
    is_subscribed = serializers.BooleanField()

    class Meta:
        model = ApiUser
        fields = (
            'email',
            'id',
            'username',
            'first_name',
            'last_name',
            'is_subscribed'
        )


class SubscriptionSerializerForWrite(serializers.ModelSerializer):
    user = serializers.StringRelatedField(
        required=False,
        read_only=True,
        default=serializers.CurrentUserDefault()
    )
    subscription = serializers.PrimaryKeyRelatedField(
        read_only=True
    )

    class Meta:
        model = Subscription
        fields = ('user', 'subscription')

    def validate(self, attrs):
        attrs['user'] = self.context['request'].user
        attrs['subscription_id'] = self.context['subscription_id']
        if self.context['request'].user.id == self.context['subscription_id']:
            raise serializers.ValidationError(
                'Cannot subscribe to yourself'
                )
        return attrs
    
    def to_representation(self, instance):
        return SubscriptionSerializerForRead(
            instance=instance.subscription
        ).data

成功订阅后,我需要返回包含订阅用户数据和附加值的响应

is_subscribed
。我尝试通过视图集中的
annotate
查询集来做到这一点,但作为响应我总是收到错误:

backend-1   | AttributeError: Got AttributeError when attempting to get a value for field `is_subscribed` on serializer `SubscriptionSerializerForRead`.
backend-1   | The serializer field might be named incorrectly and not match any attribute or key on the `ApiUser` instance.
backend-1   | Original exception text was: 'ApiUser' object has no attribute 'is_subscribed'.

我怎样才能完成这项工作以及导致该问题的原因是什么?相同的注释方法适用于我项目中的其他 m2m 相关模型,但对于用户模型,它由于某种原因失败

django django-models django-rest-framework django-serializer drf-queryset
1个回答
0
投票

问题出在

to_representation
类中的
SubscriptionSerializerForWrite
函数,该函数正在调用
SubscriptionSerializerForRead
类,但从序列化器创建实例时未添加注释。

解决方案可以是:

  • is_subscribed
    设置为可选。将其更改为
    SerializerMethodField
    并编写自定义逻辑,例如
    return getattr(obj, 'is_subscribed', False)

  • 创建后手动设置

    is_subscribed
    属性(或在
    to_representation
    函数中):

    def create(self, validated_data):
        instance = super().create(validated_data)
        setattr(instance, 'is_subscribed', True)
        return instance
    
© www.soinside.com 2019 - 2024. All rights reserved.