使用Sqlite3运行Django Unittest时丢失了表

时间:2022-05-02 00:16:24

I'm trying to run a unittest with Django 1.3. Normally, I use MySQL as my database backend, but since this is painfully slow to spinup for a single unittest, I'm using Sqlite3.

我正在尝试用Django 1.3运行一个unittest。通常,我使用MySQL作为数据库后端,但由于对单个unittest进行派生操作非常缓慢,所以我使用Sqlite3。

So to switch to Sqlite3 just for my unittests, in my settings.py I have:

在设置中切换到Sqlite3。py我有:

import sys
if 'test' in sys.argv:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME':'/tmp/database.db',
            'USER'       : '',
            'PASSWORD' : '',
            'HOST'     : '',
        }
    }

When I run my unittest with python manage.py test myapp.Test.test_myfunc, I get the error:

当我使用python管理运行unittest时。py myapp.Test测试。test_myfunc,我得到了错误:

DatabaseError: no such table: django_content_type

Googling shows there are a few of possible reasons for this error, none of which seem applicable to me. I'm not running Apache, so I don't see how permissions would be an issue. The file /tmp/database.db is being created, so /tmp is writable. The app django.contrib.contenttypes is included in my INSTALLED_APPS.

谷歌搜索显示,出现这种错误有一些可能的原因,但对我来说都不适用。我没有运行Apache,所以我不认为权限会是一个问题。文件/ tmp /数据库。db正在被创建,所以/tmp是可写的。app django.contrib。在我的INSTALLED_APPS中包含了contenttypes。

What am I missing?

我缺少什么?

Edit: I ran into this problem again in Django 1.5, but none of the proposed solutions work.

编辑:我在Django 1.5中再次遇到了这个问题,但是所提出的解决方案都不起作用。

10 个解决方案

#1


12  

In Django 1.4, 1.5, 1.6, 1.7, or 1.8 it should be sufficient to use:

在Django 1.4中,1.5、1.6、1.7或1.8应该足以使用:

if 'test' in sys.argv:
    DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3'

It should not be necessary to override TEST_NAME1, nor to call syncdb in order to run tests. As @osa points out, the default with the SQLite engine is to create the test database in memory (TEST_NAME=':memory:'). Calling syncdb should not be necessary because Django's test framework will do this automatically via a call to syncdb or migrate depending on the Django version.2 You can observe this with manage.py test -v [2|3].

不应该重写TEST_NAME1,也不应该为了运行测试而调用syncdb。正如@osa指出的,SQLite引擎的默认设置是在内存中创建测试数据库(TEST_NAME=':memory:')。调用syncdb不应该是必要的,因为Django的测试框架会自动通过调用syncdb来实现,或者根据Django版本迁移。你可以用manage观察这个。py - v测试[2 | 3]。

Very loosely speaking Django sets up the test environment by:

非常松散地说,Django通过:

  1. Loading the regular database NAME from your settings.py
  2. 从settings.py中加载常规的数据库名
  3. Discovering and constructing your test classes (__init__() is called)
  4. 发现并构建测试类(__init__())
  5. Setting the database NAME to the value of TEST_NAME
  6. 将数据库名设置为TEST_NAME的值
  7. Running the tests against the database NAME
  8. 对数据库名运行测试

Here's the rub: At step 2, NAME is still pointing at your regular (non-test) database. If your tests contain class-level queries or queries in __init__(), they will be run against the regular database which is likely not what you are expecting. This is identified in bug #21143.

问题是:在步骤2中,名称仍然指向您的常规(非测试)数据库。如果您的测试包含__init__()中的类级查询或查询,那么它们将针对常规数据库运行,这很可能不是您所期望的。这是在bug #21143中发现的。

Don't do:

不要做:

class BadFooTests(TestCase):
    Foo.objects.all().delete()     # <-- class level queries, and

    def __init__(self):
        f = Foo.objects.create()   # <-- queries in constructor
        f.save()                   #     will run against the production DB

    def test_foo(self):
        # assert stuff

since these will be run against the database specified in NAME. If NAME at this stage points to a valid database (e.g. your production database), the query will run, but may have unintended consequences. If you have overridden ENGINE and/or NAME such that it does not point to a pre-existing database, an exception will be thrown because the test database has yet to be created:

因为这些将在名称中指定的数据库上运行。如果在此阶段的名称指向一个有效的数据库(例如,您的生产数据库),查询将会运行,但可能会产生意想不到的后果。如果您重写了引擎和/或名称,使其不指向已存在的数据库,则将抛出异常,因为还没有创建测试数据库:

django.db.utils.DatabaseError: no such table: yourapp_foo  # Django 1.4
DatabaseError: no such table: yourapp_foo                  # Django 1.5
OperationalError: no such table: yourapp_foo               # Django 1.6+

Instead do:

而不是做的事:

class GoodFooTests(TestCase):

    def setUp(self):
        f = Foo.objects.create()   # <-- will run against the test DB
        f.save()                   #

    def test_foo(self):
        # assert stuff

So, if you are seeing errors, check to see that your tests do not include any queries that might hit the database outside of your test class method definitions.

因此,如果您看到错误,请检查您的测试是否不包含任何可能会在测试类方法定义之外攻击数据库的查询。


[1] In Django >= 1.7, DATABASES[alias]['TEST_NAME'] is deprecated in favour of DATABASES[alias]['TEST']['NAME']
[2] See the create_test_db() method in db/backends/creation.py

[1]在Django >= 1.7中,数据库[alias]['TEST_NAME']被弃用,以支持数据库[alias]['TEST']]['NAME'] [2]

#2


4  

I had this problem, too. Turned out that I had to add a TEST_NAME property in the settings.py file to identify the test database properly. It solved the problem for me:

我也有这个问题。结果我不得不在设置中添加TEST_NAME属性。py文件正确识别测试数据库。它为我解决了问题:

if 'test' in sys.argv:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(os.path.dirname(__file__), 'test.db'),
            'TEST_NAME': os.path.join(os.path.dirname(__file__), 'test.db'),
       }
    }

#3


4  

Just to add another case to this:

再加上一个例子

If you are trying to upgrade to 1.8 from 1.6 (or from a non-migration setup to a migration setup), you might hit this error if you haven't run created migrations.

如果您试图从1.6升级到1.8(或者从非迁移设置升级到迁移设置),如果没有运行创建的迁移,您可能会遇到这个错误。

I had the same problem and had to create migrations so the test runner could use them, which was not intuitive because pre-migrations, the tests would just make a new DB based on doing syncdb, which always worked.

我遇到了同样的问题,必须创建迁移,以便测试运行人员可以使用它们,这不是直观的,因为前迁移,测试将基于执行syncdb创建一个新的DB,这总是有效的。

#4


3  

For future reference, this also happens if your application is not added to your INSTALLED_APPS, for example:

对于将来的参考,如果您的应用程序没有添加到您的INSTALLED_APPS,例如:

INSTALLED_APPS = (
   ...
   'myapp'
)

Otherwise you get;

否则你得到;

OperationalError: no such table: myapp_mytable

#5


2  

Having tried all of the above I eventually discovered another reason this can happen:-

在尝试了以上所有的方法之后,我最终发现了另一个原因:-

If any of your models are not created by one of your migrations.

如果您的任何模型不是由您的迁移创建的。

I did some debugging and it seems that Django testing sets up the database by applying all your migrations in order, starting with 001_initial.py, before trying to SELECT from the tables based on your models.py

我进行了一些调试,似乎Django测试通过按顺序应用所有迁移来设置数据库,从001_initial开始。在尝试根据您的model .py从表中选择之前

In my case a table had somehow not been added to the migrations, but added manually, so the full migration set couldn't be properly applied. When I manually fixed the 001_initial.py migration to create this table the OperationalError went away.

在我的例子中,一个表没有被添加到迁移中,而是手动添加,因此无法正确地应用完整的迁移集。当我手动修复001_initial时。py迁移创建此表的操作错误消失了。

#6


1  

Your database is probably empty, it must be setup with all the tables corresponding to your models. Normally, this would be done by running python manage.py syncdb first, to create all your database tables. The problem is that in your case, when you run syncdb, python will not see that you are running a test so it will try to setup tables in your MySQL database instead.

您的数据库可能是空的,必须使用与模型对应的所有表进行设置。通常,这将通过运行python管理来完成。py syncdb首先创建所有的数据库表。问题是,在您的情况下,当您运行syncdb时,python将不会看到您正在运行一个测试,因此它将尝试在您的MySQL数据库中设置表。

To get around this, temporarily change

为了解决这个问题,暂时改变一下

if 'test' in sys.argv:

to

if True:

Then run python manage.py syncdb to setup the sqlite database tables. Now that everything is setup, you can put back in if 'test'... and everything should run smoothly. However you probably want to move your database out of the /tmp directory: django needs to re-use the same database every time you run your tests, otherwise you'll have to create database tables before every test.

然后运行python管理。py syncdb用于设置sqlite数据库表。现在一切都准备好了,你可以输入if 'test…一切都应该顺利进行。但是,您可能希望将数据库移出/tmp目录:django需要在每次运行测试时重用相同的数据库,否则您必须在每次测试之前创建数据库表。

Note that if you add new models, you will need to repeat this procedure to create the new tables in sqlite. If you add new fields to an existing model, you will need to manually add columns to your sqlite database using the sqlite interface and ALTER TABLE..., or do it automatically using a tool like South.

注意,如果您添加了新模型,您将需要重复此过程,以在sqlite中创建新表。如果向现有模型添加新字段,则需要使用sqlite接口和ALTER TABLE手动向您的sqlite数据库添加列……或者自动使用像南方这样的工具。

#7


0  

I had to add the follwoing lines after test database definition:

在测试数据库定义之后,我必须添加以下代码行:

from django.core.management import call_command
call_command('syncdb', migrate=True)

#8


0  

I had a similar problem and I was caused by a previous branch code, so I fixed it removing the pyc files in my projects:

我有一个类似的问题,我是由以前的分支代码引起的,所以我修复了它,删除了我项目中的pyc文件:

find -regex .*pyc | xargs sudo rm

#9


-1  

For those who tried all possible ways but still stuck in this:

对于那些尝试了所有可能的方法但仍然坚持这样做的人:

Since our test code is still running in other machine but not mine, I tried to:

由于我们的测试代码仍然在其他机器上运行,而不是我的机器上,我试图:

  • Create a new virtual-env. (so isolate the affect of apps)
  • 创建一个新的virtual-env。(所以隔离应用的影响)
  • Clone a new repository. (isolate the affect of ignored files)
  • 克隆一个新的存储库。(隔离被忽略文件的影响)
  • Set in settings to use sqlite3 instead of psql (isolate the affect of database, and database settings)
  • 设置为使用sqlite3而不是psql(隔离数据库的影响和数据库设置)
  • Check env variables, and .env file (since I used foreman)
  • 检查env变量和.env文件(因为我使用了foreman)

None of those helped. Until I:

这些帮助。直到我:

  1. Create a new account on my machine. (So start clean)
  2. 在我的机器上创建一个新帐户。(所以开始清洁)
  3. Clone the repository.
  4. 克隆存储库。
  5. Run tests -> Success ????
  6. ????>运行测试成功
  7. Go back to my main account.
  8. 回到我的主账户。
  9. Open a new terminal (kill current tmux server if you're using them).
  10. 打开一个新的终端(如果正在使用当前的tmux服务器,则关闭它)。
  11. Run tests -> Success ????
  12. ????>运行测试成功

So this is not a real answer for your question, since I don't know what was wrong and how it fixed, just another suggestion that you may try.

所以这并不是你问题的真正答案,因为我不知道哪里出了问题,也不知道它是如何解决的,这只是另一个你可以尝试的建议。

#10


-1  

For anyone that will get here, searching for why Django keeps creating a database regardless of the --keepdb option. Why it says Using existing test database for alias 'default', but then runs a bunch of CREATE TABLE statements.

对于任何到这里的人,搜索为什么Django一直在创建一个数据库,而不管—keepdb选项。为什么要使用现有的测试数据库来使用别名“default”,然后运行一些CREATE TABLE语句。

If you don't set a DATABASES > default > TEST > NAME setting, Django will try to use in memory database, and it wont be kept, so set this and override a defaults.

如果不设置数据库>默认>测试>名称设置,Django将尝试在内存数据库中使用它,并且不会保留它,因此设置它并覆盖一个默认值。

You can make it like this:

你可以这样做:

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(ROOT_DIR, 'data', 'db.dev.sqlite3'), 'TEST': { 'NAME': os.path.join(ROOT_DIR, 'data', 'db.test.sqlite3'), } } }

数据库= {'default': {'ENGINE': 'django.db.backends。sqlite3”、“名称”:os.path。加入(ROOT_DIR“数据”,“db.dev.sqlite3”),“测试”:{“名称”:os.path。连接(ROOT_DIR, 'data', 'db.test.sqlite3'),}}

#1


12  

In Django 1.4, 1.5, 1.6, 1.7, or 1.8 it should be sufficient to use:

在Django 1.4中,1.5、1.6、1.7或1.8应该足以使用:

if 'test' in sys.argv:
    DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3'

It should not be necessary to override TEST_NAME1, nor to call syncdb in order to run tests. As @osa points out, the default with the SQLite engine is to create the test database in memory (TEST_NAME=':memory:'). Calling syncdb should not be necessary because Django's test framework will do this automatically via a call to syncdb or migrate depending on the Django version.2 You can observe this with manage.py test -v [2|3].

不应该重写TEST_NAME1,也不应该为了运行测试而调用syncdb。正如@osa指出的,SQLite引擎的默认设置是在内存中创建测试数据库(TEST_NAME=':memory:')。调用syncdb不应该是必要的,因为Django的测试框架会自动通过调用syncdb来实现,或者根据Django版本迁移。你可以用manage观察这个。py - v测试[2 | 3]。

Very loosely speaking Django sets up the test environment by:

非常松散地说,Django通过:

  1. Loading the regular database NAME from your settings.py
  2. 从settings.py中加载常规的数据库名
  3. Discovering and constructing your test classes (__init__() is called)
  4. 发现并构建测试类(__init__())
  5. Setting the database NAME to the value of TEST_NAME
  6. 将数据库名设置为TEST_NAME的值
  7. Running the tests against the database NAME
  8. 对数据库名运行测试

Here's the rub: At step 2, NAME is still pointing at your regular (non-test) database. If your tests contain class-level queries or queries in __init__(), they will be run against the regular database which is likely not what you are expecting. This is identified in bug #21143.

问题是:在步骤2中,名称仍然指向您的常规(非测试)数据库。如果您的测试包含__init__()中的类级查询或查询,那么它们将针对常规数据库运行,这很可能不是您所期望的。这是在bug #21143中发现的。

Don't do:

不要做:

class BadFooTests(TestCase):
    Foo.objects.all().delete()     # <-- class level queries, and

    def __init__(self):
        f = Foo.objects.create()   # <-- queries in constructor
        f.save()                   #     will run against the production DB

    def test_foo(self):
        # assert stuff

since these will be run against the database specified in NAME. If NAME at this stage points to a valid database (e.g. your production database), the query will run, but may have unintended consequences. If you have overridden ENGINE and/or NAME such that it does not point to a pre-existing database, an exception will be thrown because the test database has yet to be created:

因为这些将在名称中指定的数据库上运行。如果在此阶段的名称指向一个有效的数据库(例如,您的生产数据库),查询将会运行,但可能会产生意想不到的后果。如果您重写了引擎和/或名称,使其不指向已存在的数据库,则将抛出异常,因为还没有创建测试数据库:

django.db.utils.DatabaseError: no such table: yourapp_foo  # Django 1.4
DatabaseError: no such table: yourapp_foo                  # Django 1.5
OperationalError: no such table: yourapp_foo               # Django 1.6+

Instead do:

而不是做的事:

class GoodFooTests(TestCase):

    def setUp(self):
        f = Foo.objects.create()   # <-- will run against the test DB
        f.save()                   #

    def test_foo(self):
        # assert stuff

So, if you are seeing errors, check to see that your tests do not include any queries that might hit the database outside of your test class method definitions.

因此,如果您看到错误,请检查您的测试是否不包含任何可能会在测试类方法定义之外攻击数据库的查询。


[1] In Django >= 1.7, DATABASES[alias]['TEST_NAME'] is deprecated in favour of DATABASES[alias]['TEST']['NAME']
[2] See the create_test_db() method in db/backends/creation.py

[1]在Django >= 1.7中,数据库[alias]['TEST_NAME']被弃用,以支持数据库[alias]['TEST']]['NAME'] [2]

#2


4  

I had this problem, too. Turned out that I had to add a TEST_NAME property in the settings.py file to identify the test database properly. It solved the problem for me:

我也有这个问题。结果我不得不在设置中添加TEST_NAME属性。py文件正确识别测试数据库。它为我解决了问题:

if 'test' in sys.argv:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(os.path.dirname(__file__), 'test.db'),
            'TEST_NAME': os.path.join(os.path.dirname(__file__), 'test.db'),
       }
    }

#3


4  

Just to add another case to this:

再加上一个例子

If you are trying to upgrade to 1.8 from 1.6 (or from a non-migration setup to a migration setup), you might hit this error if you haven't run created migrations.

如果您试图从1.6升级到1.8(或者从非迁移设置升级到迁移设置),如果没有运行创建的迁移,您可能会遇到这个错误。

I had the same problem and had to create migrations so the test runner could use them, which was not intuitive because pre-migrations, the tests would just make a new DB based on doing syncdb, which always worked.

我遇到了同样的问题,必须创建迁移,以便测试运行人员可以使用它们,这不是直观的,因为前迁移,测试将基于执行syncdb创建一个新的DB,这总是有效的。

#4


3  

For future reference, this also happens if your application is not added to your INSTALLED_APPS, for example:

对于将来的参考,如果您的应用程序没有添加到您的INSTALLED_APPS,例如:

INSTALLED_APPS = (
   ...
   'myapp'
)

Otherwise you get;

否则你得到;

OperationalError: no such table: myapp_mytable

#5


2  

Having tried all of the above I eventually discovered another reason this can happen:-

在尝试了以上所有的方法之后,我最终发现了另一个原因:-

If any of your models are not created by one of your migrations.

如果您的任何模型不是由您的迁移创建的。

I did some debugging and it seems that Django testing sets up the database by applying all your migrations in order, starting with 001_initial.py, before trying to SELECT from the tables based on your models.py

我进行了一些调试,似乎Django测试通过按顺序应用所有迁移来设置数据库,从001_initial开始。在尝试根据您的model .py从表中选择之前

In my case a table had somehow not been added to the migrations, but added manually, so the full migration set couldn't be properly applied. When I manually fixed the 001_initial.py migration to create this table the OperationalError went away.

在我的例子中,一个表没有被添加到迁移中,而是手动添加,因此无法正确地应用完整的迁移集。当我手动修复001_initial时。py迁移创建此表的操作错误消失了。

#6


1  

Your database is probably empty, it must be setup with all the tables corresponding to your models. Normally, this would be done by running python manage.py syncdb first, to create all your database tables. The problem is that in your case, when you run syncdb, python will not see that you are running a test so it will try to setup tables in your MySQL database instead.

您的数据库可能是空的,必须使用与模型对应的所有表进行设置。通常,这将通过运行python管理来完成。py syncdb首先创建所有的数据库表。问题是,在您的情况下,当您运行syncdb时,python将不会看到您正在运行一个测试,因此它将尝试在您的MySQL数据库中设置表。

To get around this, temporarily change

为了解决这个问题,暂时改变一下

if 'test' in sys.argv:

to

if True:

Then run python manage.py syncdb to setup the sqlite database tables. Now that everything is setup, you can put back in if 'test'... and everything should run smoothly. However you probably want to move your database out of the /tmp directory: django needs to re-use the same database every time you run your tests, otherwise you'll have to create database tables before every test.

然后运行python管理。py syncdb用于设置sqlite数据库表。现在一切都准备好了,你可以输入if 'test…一切都应该顺利进行。但是,您可能希望将数据库移出/tmp目录:django需要在每次运行测试时重用相同的数据库,否则您必须在每次测试之前创建数据库表。

Note that if you add new models, you will need to repeat this procedure to create the new tables in sqlite. If you add new fields to an existing model, you will need to manually add columns to your sqlite database using the sqlite interface and ALTER TABLE..., or do it automatically using a tool like South.

注意,如果您添加了新模型,您将需要重复此过程,以在sqlite中创建新表。如果向现有模型添加新字段,则需要使用sqlite接口和ALTER TABLE手动向您的sqlite数据库添加列……或者自动使用像南方这样的工具。

#7


0  

I had to add the follwoing lines after test database definition:

在测试数据库定义之后,我必须添加以下代码行:

from django.core.management import call_command
call_command('syncdb', migrate=True)

#8


0  

I had a similar problem and I was caused by a previous branch code, so I fixed it removing the pyc files in my projects:

我有一个类似的问题,我是由以前的分支代码引起的,所以我修复了它,删除了我项目中的pyc文件:

find -regex .*pyc | xargs sudo rm

#9


-1  

For those who tried all possible ways but still stuck in this:

对于那些尝试了所有可能的方法但仍然坚持这样做的人:

Since our test code is still running in other machine but not mine, I tried to:

由于我们的测试代码仍然在其他机器上运行,而不是我的机器上,我试图:

  • Create a new virtual-env. (so isolate the affect of apps)
  • 创建一个新的virtual-env。(所以隔离应用的影响)
  • Clone a new repository. (isolate the affect of ignored files)
  • 克隆一个新的存储库。(隔离被忽略文件的影响)
  • Set in settings to use sqlite3 instead of psql (isolate the affect of database, and database settings)
  • 设置为使用sqlite3而不是psql(隔离数据库的影响和数据库设置)
  • Check env variables, and .env file (since I used foreman)
  • 检查env变量和.env文件(因为我使用了foreman)

None of those helped. Until I:

这些帮助。直到我:

  1. Create a new account on my machine. (So start clean)
  2. 在我的机器上创建一个新帐户。(所以开始清洁)
  3. Clone the repository.
  4. 克隆存储库。
  5. Run tests -> Success ????
  6. ????>运行测试成功
  7. Go back to my main account.
  8. 回到我的主账户。
  9. Open a new terminal (kill current tmux server if you're using them).
  10. 打开一个新的终端(如果正在使用当前的tmux服务器,则关闭它)。
  11. Run tests -> Success ????
  12. ????>运行测试成功

So this is not a real answer for your question, since I don't know what was wrong and how it fixed, just another suggestion that you may try.

所以这并不是你问题的真正答案,因为我不知道哪里出了问题,也不知道它是如何解决的,这只是另一个你可以尝试的建议。

#10


-1  

For anyone that will get here, searching for why Django keeps creating a database regardless of the --keepdb option. Why it says Using existing test database for alias 'default', but then runs a bunch of CREATE TABLE statements.

对于任何到这里的人,搜索为什么Django一直在创建一个数据库,而不管—keepdb选项。为什么要使用现有的测试数据库来使用别名“default”,然后运行一些CREATE TABLE语句。

If you don't set a DATABASES > default > TEST > NAME setting, Django will try to use in memory database, and it wont be kept, so set this and override a defaults.

如果不设置数据库>默认>测试>名称设置,Django将尝试在内存数据库中使用它,并且不会保留它,因此设置它并覆盖一个默认值。

You can make it like this:

你可以这样做:

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(ROOT_DIR, 'data', 'db.dev.sqlite3'), 'TEST': { 'NAME': os.path.join(ROOT_DIR, 'data', 'db.test.sqlite3'), } } }

数据库= {'default': {'ENGINE': 'django.db.backends。sqlite3”、“名称”:os.path。加入(ROOT_DIR“数据”,“db.dev.sqlite3”),“测试”:{“名称”:os.path。连接(ROOT_DIR, 'data', 'db.test.sqlite3'),}}