Django单元测试中对象ID递增的问题

5 投票
1 回答
2107 浏览
提问于 2025-04-16 19:01

我在 Debian squeeze 上使用 Django 1.2.3-3+squeeze1 和 PostgreSQL 8.4.7-0squeeze2(不过我觉得 PostgreSQL 在这里并不重要),并且在运行基于 unittest 的 Django 单元测试,使用了以下的 setUp 和 tearDown 方法。

   def setUp(self):
        print "running setup"
        self.c = Client()
        self.user = User.objects.create_user('faheem', 'faheem@email.unc.edu', 'foo')
        self.logged_in = self.c.login(username='faheem', password='foo')
        settings.MEDIA_ROOT='/tmp/'
        #settings.ZIP_UPLOAD='/var/tmp/zip/'

    def tearDown(self):
        print "running teardown"
        FolderUpload.objects.all().delete()
        FileUpload.objects.all().delete()
        ZipFileUpload.objects.all().delete()
        OldFileUpload.objects.all().delete()
        # FIXME: Quick & dirty fix for the time being. Should make this a delete method.
        os.system("rm -rf "+ settings.ZIP_UPLOAD + "/*")

我的想法是,在运行单元测试之间,数据库中的所有内容都应该被清除。根据 unittest 的文档,tearDown 就是用来做这个的。问题是,我发现不同的单元测试之间似乎还有一些状态被保留。具体来说,我看到 id 是递增的。比如说,如果我在 test1 中创建一个 ZipFileUpload 对象,然后在 test2 中再创建一个 ZipFileUpload 对象,我本来希望这两个对象的 id 都是 1,但实际上我看到的是 test1 的 id 是 1,而 test2 的 id 是 2。如果这些 id 是来自某个在这些表之外的索引,那就说得通了。我对 Django 是怎么处理这个的了解不够,所以不确定是否真是这样。如果真是这样,我也不知道为什么会这样。对此如果能有些解释就太好了。

不管怎样,我希望能找到一种干净的方法来删除数据库,如果有人能推荐一个方法就好了。这个方法应该放在 tearDown 中。测试 Django 应用程序 提到了一下这个函数,但我没能从 django.test.utils 中导入它。令人困惑的是,这个函数似乎在 django/db/backends/creation.py 中。

destroy_test_db(old_database_name, verbosity=1)

销毁数据库,数据库的名称存储在 DATABASES 中的 NAME,并将 NAME 设置为使用提供的名称。

这句话的前半部分是可以理解的 - “销毁数据库,数据库的名称存储在 DATABASES 中的 NAME”,但“将 NAME 设置为使用提供的名称”是什么意思呢?我假设提供的名称是 old_database_name

在这个上下文中,NAME 是什么并不清楚。它是 DATABASES 中的 NAME 吗?如果是的话,为什么我还需要设置一个已经设置好的东西?我假设提供的名称是 old_database_name,但如果是这样,为什么我还要把它设置为一个叫 old_database_name 的参数呢?这句话在开发文档中没有改变。

编辑:

在收到 Steve Mayne 的回复后(见下文),我想稍微详细说明一下背景。

这个应用程序最初是在 2007/2008/2009 年间编写的,包括单元测试。在那段时间里,我使用的是 Django 1.0 之前的版本。根据 Ken Cochran 的 Django 发布历史,1.0 版本是在 2008 年 9 月 3 日发布的。描述的设置在那段时间内运行得很好。我看到上面的 tearDown 函数最初是在 2007 年 12 月写的。所以,也许 Django 的行为发生了变化?

事后看来,我意识到像上面 tearDown 所做的那样清空表,并不能保证 id 计数会重置为 1,因为序列可能是与表分开的一个对象。

感谢 Steve 提供的解决方案。如果有的话,我想听听关于序列重置的可移植解决方案。我也想了解如何让上面的 destroy_test_db 函数工作。

1 个回答

5

你可以用下面的SQL语句来重置每个表的ID序列:

SELECT pg_catalog.setval(pg_get_serial_sequence('table_name', 'id'), 1);

不过,只有在你的表是空的时候才应该这样做。

撰写回答