我有一个自定义的用户模型和订阅模型,其中包含ForeignKeys to subscribe和user to subscribe.

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

此外,我有用于订阅的viewset和两个序列化器:写和读.

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相关模型,但用户模型由于某种原因而失败

推荐答案

问题出在SubscriptionSerializerForWrite类中的to_representation函数,它正在调用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
    

Django相关问答推荐

如何在Django中更改模型字段名称?

未显示SWAGER上的一些示例架构

如何在Django查询集中查询多对多字段内是否存在实例

Django:添加第二个';详细';用于检索额外信息的视图

更改Django的创建时间字段

使用OuterRef过滤器获取Django记录的最大值

无法创建超级用户,因为 Django 中的一列(外键)不能为空

Django 转储数据将附加数据添加到 json 导出

如何在Django中制作一个不 Select 以前日期的日期 Select 器

Pytest-django - 测试创建和传递所需的用户对象

在模板中调用 FileField 对象会呈现不正确的路径

多租户 Django 应用程序:根据请求更改数据库连接?

Django 视图 - 首先从调用应用程序的目录加载模板

如何在 Django 视图中获取 URL 参数?

Django中的单表继承

Django 什么是反向关系?

Django将HttpResponseRedirect返回到带有参数的url

Django:以 10 为底的 int() 的无效文字

如何过滤(或替换)在 UTF-8 中占用超过 3 个字节的 unicode 字符?

Heroku & Django:OSError: No such file or directory:'/app/{myappname}/static'