Django序列化报错:QuerySet实例无url属性问题求助
解决Django REST Framework获取用户密码时的AttributeError错误
问题现象
尝试通过http://localhost:8000/password?user_id=1获取指定用户的所有密码数据时,出现以下错误:
AttributeError at /password/
Got AttributeError when attempting to get a value for fieldurlon serializerPasswordSerializer.
The serializer field might be named incorrectly and not match any attribute or key on theQuerySetinstance.
Original exception text was: 'QuerySet' object has no attribute 'url'.
错误原因
- 序列化QuerySet未指定
many=True:Password.objects.filter(user=user)返回的是包含多个对象的QuerySet,但PasswordSerializer默认处理单个模型实例,直接序列化QuerySet会导致字段找不到的错误。 - 请求参数不匹配:测试URL传递的参数是
user_id,但代码中用request.GET.get('user')获取,导致查询参数为空,无法正确筛选用户。 - 无效的异常捕获:
filter()方法不会抛出DoesNotExist异常,只有get()方法才会,原代码中的try-except块起不到作用。 - 模型默认值错误:
password = models.CharField(default=pass_gen(), max_length=100)中,pass_gen()会在模型加载时执行一次,所有新创建的Password实例会共享同一个初始密码,应该传递函数名而非调用结果。
修复步骤
1. 修改models.py中的Password模型
修正密码默认值的赋值方式:
from django.db import models import random def pass_gen(): password = '' def rand_uni(): return random.randint(33, 126) def rand(): return random.randint(8, 20) code = rand() for num in range(code): r_int = chr(rand_uni()) password += r_int return password class User(models.Model): username = models.CharField(max_length=200) class Password(models.Model): url = models.CharField(max_length=800) # 改为传递函数名,每次创建实例时都会调用生成新密码 password = models.CharField(default=pass_gen, max_length=100) user = models.ForeignKey(User, on_delete=models.CASCADE) class Meta: constraints = [ models.UniqueConstraint(fields=['url', 'user'], name='unique_url_and_user_id') ]
2. 修改views.py中的passwords_one_user视图函数
修复参数匹配、序列化参数和异常处理:
from django.shortcuts import render from django.http import HttpResponse, JsonResponse from rest_framework.parsers import JSONParser from vault.models import User, Password from django.views.decorators.csrf import csrf_exempt from vault.serializers import UserSerializer, PasswordSerializer # 其他视图函数保持不变... @csrf_exempt def passwords_one_user(request): user_id = request.GET.get('user_id') url = request.GET.get('url') # 处理获取单个用户所有密码的情况 if request.method == "GET": if not user_id: return JsonResponse({"error": "user_id参数必填"}, status=400) # 筛选该用户的所有密码 passwords = Password.objects.filter(user_id=user_id) if not passwords.exists(): return HttpResponse(status=404) # 序列化多个对象必须加many=True serializer = PasswordSerializer(passwords, many=True) return JsonResponse(serializer.data, safe=False) elif request.method == 'POST': data = JSONParser().parse(request) serializer = PasswordSerializer(data=data) if serializer.is_valid(): serializer.save() return JsonResponse(serializer.data, status=201) return JsonResponse(serializer.errors, status=400) # 处理单个密码的PUT/DELETE操作(需要同时指定user_id和url) elif request.method in ['PUT', 'DELETE']: if not (user_id and url): return JsonResponse({"error": "需要同时提供user_id和url参数"}, status=400) try: password = Password.objects.get(user_id=user_id, url=url) except Password.DoesNotExist: return HttpResponse(status=404) if request.method == 'PUT': data = JSONParser().parse(request) serializer = PasswordSerializer(password, data=data) if serializer.is_valid(): serializer.save() return JsonResponse(serializer.data) return JsonResponse(serializer.errors, status=400) elif request.method == 'DELETE': password.delete() return HttpResponse(status=204)
3. 可选:简化Serializer(非必需)
原Serializer中手动定义url字段是多余的,因为ModelSerializer会自动根据模型字段生成,可简化为:
from rest_framework import serializers from vault.models import User, Password class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = '__all__' class PasswordSerializer(serializers.ModelSerializer): class Meta: model = Password fields = ('url', 'password', "user")
验证修复
重新启动服务后,访问http://localhost:8000/password?user_id=1即可正确返回该用户关联的所有密码数据。
内容的提问来源于stack exchange,提问作者raven
相关产品推荐
相关产品推荐

