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

Django测试中出现'Duplicate entry'错误的原因排查

问题描述

原本运行正常的Django单元测试,添加向MySQL发送原生查询的代码后,所有涉及认证的单元测试均抛出异常,即使移除这些代码问题仍存在。

错误信息

_mysql.connection.query(self, query)
django.db.utils.IntegrityError: (1062, "Duplicate entry 'myuser' for key 'auth_user.username'")

测试代码(添加查询前)

class AnnotationPage(TestCase):
    def setUp(self):
        self.factory = RequestFactory()
        self.user = User.objects.create_user(
            username='myuser', password='12345678')


    def test_labeling(self):
        ajaxData = {'testing': 'True',
                    'direction': 'forward'}
        request = self.factory.post('/annotation', ajaxData)
        request.user = self.user
        response = AnnotationView(request)
        assert response.content == b'{"image": "image.png", "id": 1}'

测试代码(添加查询后)

class AnnotationPage(TestCase):
    def setUp(self):
        self.factory = RequestFactory()
        self.user = User.objects.create_user(
            username='myuser', password='12345678')
        self.cursor = connection.cursor()
        query = "TRUNCATE TABLE homepage_imagelabel"
        self.cursor.execute(query)


    def test_labeling(self):
        self.cursor = connection.cursor()
        query = "INSERT INTO homepage_imagelabel (ImageFile) VALUES ('image.png')"
        self.cursor.execute(query)
        ajaxData = {'testing': 'True',
                    'direction': 'forward'}
        request = self.factory.post('/annotation', ajaxData)
        request.user = self.user
        response = AnnotationView(request)
        assert response.content == b'{"image": "image.png", "id": 1}'
问题原因

核心问题是**TRUNCATE语句会隐式提交事务**,而Django的TestCase默认依赖事务隔离每个测试用例:每个测试开始时开启事务,测试结束后回滚事务,以此保证测试数据不会互相污染。

当你在setUp里执行TRUNCATE时,这个操作会强制提交当前事务,直接破坏了Django的事务隔离机制——之前测试创建的myuser没有被回滚,仍然留在数据库中。后续测试再执行User.objects.create_user创建同名用户时,就触发了唯一键冲突。

即使你移除了TRUNCATE代码,之前的测试已经污染了测试数据库的状态(比如auth_user表中已存在myuser),如果测试数据库没有被重新创建,问题会持续存在。

解决办法
  1. 替换TRUNCATE为Django ORM操作
    不要用原生TRUNCATE,改用Django提供的删除方法,它会遵守事务规则:

    # 替换TRUNCATE语句
    from homepage.models import ImageLabel
    ImageLabel.objects.all().delete()
    
  2. 必须用原生SQL时,改用DELETE
    如果一定要用原生SQL,用DELETE FROM代替TRUNCATE,因为DELETE不会隐式提交事务:

    query = "DELETE FROM homepage_imagelabel"
    self.cursor.execute(query)
    
  3. 重置测试数据库
    运行测试时添加--noinput参数,强制重新创建测试数据库,清理残留数据:

    python manage.py test --noinput
    

    如果你之前用了--keepdb参数保留测试库,这个操作会彻底重置数据库状态。

  4. 避免在测试中使用会隐式提交的SQL语句
    除了TRUNCATE,像ALTER TABLE、DROP TABLE这类DDL语句也会隐式提交事务,测试中尽量用Django ORM提供的方法操作数据。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 19:54:26