我有一个自定义用户模型和订阅模型,其中包含
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 相关模型,但对于用户模型,它由于某种原因失败
问题出在
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