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

Django权限控制测试报错:UserType matching query does not exist问题咨询

解决Django权限测试中的UserType DoesNotExist错误

嘿,我来帮你拆解一下这个问题,你遇到的错误其实是Django模块加载顺序和测试数据初始化时机冲突导致的,具体原因和解决方法如下:

错误根源

你的rights.py文件在模块加载阶段就执行了这些数据库查询:

scanner = UserType.objects.get(id=1)
overwatch = UserType.objects.get(id=2)
lancer = UserType.objects.get(id=3)
admin = UserType.objects.get(id=4)

而测试用例的setUp方法是在测试运行阶段才创建UserType数据的。模块加载的时间远早于测试数据初始化,所以此时测试数据库里还没有任何UserType记录,自然会抛出DoesNotExist异常。

另外,你的测试代码还有两个隐藏问题:

  1. 创建AssignedType时直接传入字符串(比如user='scanner1'),外键字段需要传入模型实例而不是字符串,这会导致外键关联失败。
  2. 创建用户用User.objects.create()而不是create_user(),这种方式不会对密码进行哈希处理,测试中的c.login()可能会失败。

解决方案

1. 修改rights.py,避免模块级别的数据库查询

把获取UserType的逻辑移到函数内部,或者用用户类型名称来定义权限,这样就不会在模块加载时触发数据库查询了。修改后的代码示例:

from .models import User, UserType, AssignedType

# 用用户类型名称定义权限,避免直接查询数据库
group_rights = {
    'scanner': ['scanner', 'admin'],
}

def is_user_permitted(logged_user, view):
    # 获取当前用户的所有权限类型名称
    assigned_type_names = list(
        AssignedType.objects.filter(user=logged_user)
        .values_list('user_type__name', flat=True)
    )
    # 获取当前视图允许的权限类型名称
    permitted_types = group_rights.get(view, [])
    # 检查是否有重叠的权限
    return any(typ in permitted_types for typ in assigned_type_names)

2. 修复测试用例的setUp方法

修改用户和AssignedType的创建方式,确保传入正确的模型实例,并且正确哈希密码:

from django.test import TestCase, Client
from django.urls import reverse
from DataMech.models import User, UserType, AssignedType

class TestScanner(TestCase):
    def setUp(self):
        # 使用create_user创建用户,自动哈希密码
        self.scanner1 = User.objects.create_user(username='scanner1', password='abc')
        self.scanner2 = User.objects.create_user(username='scanner2', password='abc')
        self.overwatch1 = User.objects.create_user(username='overwatch1', password='abc')
        self.overwatch2 = User.objects.create_user(username='overwatch2', password='abc')
        self.lancer1 = User.objects.create_user(username='lancer1', password='abc')
        self.lancer2 = User.objects.create_user(username='lancer2', password='abc')
        self.lancer3 = User.objects.create_user(username='lancer3', password='abc')
        self.lancer4 = User.objects.create_user(username='lancer4', password='abc')
        self.admin1 = User.objects.create_user(username='admin1', password='abc')
        
        # 创建UserType实例并保存为变量
        self.scanner_type = UserType.objects.create(name='scanner')
        self.overwatch_type = UserType.objects.create(name='overwatch')
        self.lancer_type = UserType.objects.create(name='lancer')
        self.admin_type = UserType.objects.create(name='admin')
        
        # 传入模型实例创建AssignedType
        AssignedType.objects.create(user=self.scanner1, user_type=self.scanner_type)
        AssignedType.objects.create(user=self.scanner2, user_type=self.scanner_type)
        AssignedType.objects.create(user=self.overwatch1, user_type=self.overwatch_type)
        AssignedType.objects.create(user=self.overwatch2, user_type=self.overwatch_type)
        AssignedType.objects.create(user=self.lancer1, user_type=self.lancer_type)
        AssignedType.objects.create(user=self.lancer2, user_type=self.lancer_type)
        AssignedType.objects.create(user=self.lancer3, user_type=self.lancer_type)
        AssignedType.objects.create(user=self.lancer4, user_type=self.lancer_type)
        AssignedType.objects.create(user=self.admin1, user_type=self.admin_type)

    def test_allow_access_only_if_user_is_authorized_GET(self):
        c = Client()
        c.login(username='scanner1', password='abc')
        response = c.get(reverse('scanner'))
        self.assertEquals(response.status_code, 200)
        self.assertTemplateUsed(response, 'DataMech/scanner.html')
        c.logout()

        c = Client()
        c.login(username='scanner2', password='abc')
        response = c.get(reverse('scanner'))
        self.assertEquals(response.status_code, 200)
        self.assertTemplateUsed(response, 'DataMech/scanner.html')
        c.logout()

        c = Client()
        c.login(username='overwatch1', password='abc')
        response = c.get(reverse('scanner'))
        self.assertEquals(response.status_code, 403)
        c.logout()

额外建议

  • 尽量避免在模块级别执行数据库查询,这种操作不仅会导致测试问题,还会影响应用启动速度,并且在数据库未初始化时会触发错误。
  • 对于权限控制,Django自带的django.contrib.auth权限系统其实已经能满足大部分需求,如果你需要更精细的分组权限,可以考虑使用django-guardian等第三方库,减少自己造轮子的成本。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 08:48:12