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

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 field url on serializer PasswordSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the QuerySet instance.
Original exception text was: 'QuerySet' object has no attribute 'url'.

错误原因

  1. 序列化QuerySet未指定many=True:Password.objects.filter(user=user)返回的是包含多个对象的QuerySet,但PasswordSerializer默认处理单个模型实例,直接序列化QuerySet会导致字段找不到的错误。
  2. 请求参数不匹配:测试URL传递的参数是user_id,但代码中用request.GET.get('user')获取,导致查询参数为空,无法正确筛选用户。
  3. 无效的异常捕获:filter()方法不会抛出DoesNotExist异常,只有get()方法才会,原代码中的try-except块起不到作用。
  4. 模型默认值错误: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 21:24:57