You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django ViewSet返回会员模型字段报无len()属性错误

问题根因

触发object of type 'Membership' has no len()报错的核心原因是视图get_queryset方法的写法不符合DRF(Django REST Framework)ModelViewSet的运行规则,具体问题如下:

  • get_queryset的返回值必须是模型对应的QuerySet集合,不能是单个模型实例。你用UserMembership.objects.get()拿到的是单条UserMembership记录,最终返回的还是单条Membership实例,DRF后续执行分页、序列化校验时会对返回值调用len()取长度,单模型实例没有长度属性,直接抛错。
  • 序列化器与返回模型不匹配:视图指定的序列化器是UserMemberShipSerializer(对应UserMembership模型),最终返回的却是Membership模型实例,就算绕过长度校验,序列化阶段也会因字段不匹配报错。
  • 边界场景未处理:objects.get()在找不到匹配的用户会员记录时会直接抛出DoesNotExist异常,现有代码只处理了记录存在的分支,记录不存在时会返回None,同样会触发运行错误。
  • 模型定义隐患:UserMembership模型中membership外键配置的default='Free'写法无效,外键默认值需要对应关联模型的主键,直接传字符串会在数据写入时报错。
修正代码

1. 修复模型外键默认值问题

class UserMembership(models.Model):
    user = models.ForeignKey(
        'accounts.User', 
        on_delete=models.CASCADE, 
        related_name='user_membership'
    )
    membership = models.ForeignKey(
        Membership, 
        on_delete=models.DO_NOTHING, 
        null=True, 
        related_name='user_relations',
        default=None
    )

    def __str__(self):
        return self.user.email

    # 保存时自动兜底:用户无关联会员时自动绑定Free类型会员
    def save(self, *args, **kwargs):
        if not self.membership:
            free_membership, _ = Membership.objects.get_or_create(
                membership_type='Free',
                defaults={'price': 0}
            )
            self.membership = free_membership
        super().save(*args, **kwargs)

2. 修复视图逻辑

如果需求只是返回当前登录用户的会员信息,不需要ModelViewSet自带的增删改查全量接口,优先用轻量的RetrieveAPIView实现:

from rest_framework.generics import RetrieveAPIView
from rest_framework.permissions import IsAuthenticated

class MembershipView(RetrieveAPIView):
    permission_classes = [IsAuthenticated]
    # 替换为你自己编写的Membership模型序列化器
    serializer_class = MembershipSerializer

    def get_object(self):
        user_membership, _ = UserMembership.objects.get_or_create(user=self.request.user)
        return user_membership.membership

如果必须使用ModelViewSet,要保证get_queryset始终返回QuerySet集合,不要返回单个实例:

class MembershipView(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated]
    serializer_class = UserMemberShipSerializer

    def get_queryset(self):
        # 过滤出当前用户对应的会员关联记录,select_related提前连表减少查询次数
        return UserMembership.objects.filter(
            user=self.request.user
        ).select_related('membership')

注意:get_queryset的作用是返回可过滤的模型集合,不要在这个方法里调用objects.get()取单条记录;如果需要获取单条实例,应该重写get_object()方法。

内容的提问来源于stack exchange,提问作者Ashti Nematian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 06:48:21