djangodjango-modelsdjango-rest-frameworkdjango-serializerdrf-queryset

Django REST framework: object has no attribute after annotation; Got AttributeError when attempting to get a value for field <field> on serializer


I have a custom user model and subscription model, which contains ForeignKeys to subscriber and user to subscribe to.

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'
    )

Also i have viewset for subscription and two serializers: for write and for read.

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

After successful subscription i need to return response with subscribed user data and additional value is_subscribed. I tried to do that through annotate queryset in my viewset, but in response i always get an error:

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'.

How could i make this work and what's causing that issue? Same annotation method works for other m2m related models in my project, but with user model it fails for some reason


Solution

  • The problem was at to_representation function in SubscriptionSerializerForWrite class which is calling SubscriptionSerializerForRead class but the instance was not annotated when created from the serializer.

    The solutions could be either: