从 1.8 升级到 2.2.4 后,Django 无法创建用于单元测试的表

Posted

技术标签:

【中文标题】从 1.8 升级到 2.2.4 后,Django 无法创建用于单元测试的表【英文标题】:Django unable to create tables for unit testing after upgrade from 1.8 to 2.2.4 【发布时间】:2020-01-06 10:55:05 【问题描述】:

从 Django 1.8 成功升级到 Django 2.2.4 后,当 我运行测试它失败了,因为 Django 没有创建数据库表。

我的 test_settings.py 文件在升级前工作正常。

调试 Django db/backends 后,看起来连接已创建,但表并未创建。 我怀疑 test_setting.py 没有配置好,尽管我从 setting.py 文件中复制了除了数据库配置(仍然是 SQLite)之外的所有内容。

数据库配置:

DATABASES = 
    'default': 
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'test_database'
    

INSTALLED_APPS = [
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'rest_framework.authtoken',
    'my_app',
    'mptt',
    'corsheaders',
    # audit app
    'easyaudit',
    'kronos',
    'django.contrib.admin',
]

我得到的回溯(例如,这里是 auth_user,但它以相同的方式为所有表重现):

Traceback (most recent call last):
  File "C:\Users\owner\Desktop\workspace\my_project\MyProject\services\tests\test_logging.py", line 33, in setUpClass
    cls.user = User.objects.get_or_create(username='logging_test_user', password='test_password')
  File "C:\Users\owner\Desktop\workspace\my_project\venv36\lib\site-packages\django\db\models\manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Users\owner\Desktop\workspace\my_project\venv36\lib\site-packages\django\db\models\query.py", line 538, in get_or_create
    return self.get(**kwargs), False
  File "C:\Users\owner\Desktop\workspace\my_project\venv36\lib\site-packages\django\db\models\query.py", line 402, in get
    num = len(clone)
  File "C:\Users\owner\Desktop\workspace\my_project\venv36\lib\site-packages\django\db\models\query.py", line 256, in __len__
    self._fetch_all()
  File "C:\Users\owner\Desktop\workspace\my_project\venv36\lib\site-packages\django\db\models\query.py", line 1242, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "C:\Users\owner\Desktop\workspace\my_project\venv36\lib\site-packages\django\db\models\query.py", line 55, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "C:\Users\owner\Desktop\workspace\my_project\venv36\lib\site-packages\django\db\models\sql\compiler.py", line 1100, in execute_sql
    cursor.execute(sql, params)
  File "C:\Users\owner\Desktop\workspace\my_project\venv36\lib\site-packages\django\db\backends\utils.py", line 67, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "C:\Users\owner\Desktop\workspace\my_project\venv36\lib\site-packages\django\db\backends\utils.py", line 76, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "C:\Users\owner\Desktop\workspace\my_project\venv36\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\owner\Desktop\workspace\my_project\venv36\lib\site-packages\django\db\utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "C:\Users\owner\Desktop\workspace\my_project\venv36\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\owner\Desktop\workspace\my_project\venv36\lib\site-packages\django\db\backends\sqlite3\base.py", line 383, in execute
    return Database.Cursor.execute(self, query, params)
django.db.utils.OperationalError: no such table: auth_user

使用环境变量执行的测试

DJANGO_SETTINGS_MODULE=MyProject.test_settings

【问题讨论】:

在运行测试时是否看到“Creating test database for alias 'default'...”? 另外,您的开发/生产数据库是什么?为什么不在你的测试中使用相同的东西呢?例如,如果您使用 PostgreSQL 数据库并使用特定的 pg 功能(如 JSONField),您的测试将失败。 我在我的环境中看不到它,但在我的同事中,它已显示,但仍然出现相同的错误。我的开发数据库是 PostgreSQL。我们没有使用 JSONField,所以这不是问题。 【参考方案1】:

这个版本的django好像有bug(可能也是2.2之前的,我没查过)。

    databases = self.get_databases(suite)
    old_config = self.setup_databases(aliases=databases)

这里的databases变量在任何情况下都不会是None,因为在get_databases函数中它调用_get_databases()并且如果它找不到任何如上所述配置的数据库,数据库将设置为set(),并且在 setup_databases 函数中有一个函数叫做

get_unique_databases_and_mirrors

检查是否没有在测试中配置数据库,如果没有,它将采用设置文件中配置的数据库,但是因为它设置为空集(set())而不是进入这种情况,因此不会创建任何数据库。

要传递该错误,您可以创建一个继承 unittests.Testcase 的类,并像这样配置数据库:

import django
import unittest
from django.conf import settings


class MyTestCase(unittest.TestCase):
    databases = settings.DATABASES

    @classmethod
    def setUpClass(cls) -> None:
        super(MyTestCase, cls).setUpClass()
        django.setup()

【讨论】:

【参考方案2】:

在某个地方,需要将测试用例使用的数据库附加到它们上。在实例化测试数据库之前,Django 根据对象列出所有需要的数据库。在此之后,它会为它们制作架构。

如果你为你的所有测试添加你继承自的基类,或者你从 Django 类子类化的每个类中添加如下一行:

databases = settings.DATABASES

你会发现你的数据库应该实例化。

【讨论】:

谢谢你,迪伦。一点点更正:您需要发送数据库字典本身,而不是发送数据库名称。当设置来自 django.conf 导入设置时,您可以通过发送 settings.DATABASES 来进行通用化

以上是关于从 1.8 升级到 2.2.4 后,Django 无法创建用于单元测试的表的主要内容,如果未能解决你的问题,请参考以下文章

升级到 Django 1.8 后“提供的固定默认值”

升级到 1.8 后进行迁移时出现 DjangoUnicodeDecodeError

从 1.8 升级到 1.9 Django Admin get_urls 不起作用

从 Django 1.6 (with south) 升级到 1.8 不会修改用户表上的“last_login”

无法将 Django 从 1.7 迁移到 1.8

升级到 django 1.7 后所有管理员网址的 NoReverseMatch