Django REST framework: объект не имеет атрибута после аннотации; получена ошибка AttributeError при попытке получить значение для поля <field> на сериализаторе
У меня есть пользовательская модель пользователя и модель подписки, которая содержит 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
queryset в моем наборе представлений, но в ответе я всегда получаю ошибку:
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, в моем проекте, но с моделью пользователя он почему-то не работает
Проблема была в 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