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
相关产品推荐
相关产品推荐

