2

How do you prevent Django unittests from running South migrations?

I have a custom Django app, myapp, that I'm trying to test with manage.py test myapp but when I run it I get the error:

django.db.utils.OperationalError: table "myapp_mymodel" already exists

and sure enough, the traceback shows that South is being executed:

File "/usr/local/myproject/.env/local/lib/python2.7/site-packages/south/management/commands/test.py", line 8, in handle
super(Command, self).handle(*args, **kwargs)

However, in my settings, I've specified:

SOUTH_TESTS_MIGRATE = 0
SKIP_SOUTH_TESTS = 1

which I believe should prevent Django's test framework from executing any South components.

What am I doing wrong?

Edit: I worked around this by simply removing south with:

if 'test' in sys.argv:
    INSTALLED_APPS.remove('south')

However, then I got:

ImproperlyConfigured: settings.DATABASES is improperly configured. Please supply the NAME value.

For my test database settings, I was using:

DATABASES = {
    'default':{
        'ENGINE': 'django.db.backends.sqlite3'
    }
}

which worked fine in Django 1.4. Now I'm using Django 1.5, and I guess that's not kosher. However, no NAME value I see it to fixes it. They all report none of my tables exist. I've tried:

DATABASES = {
    'default':{
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': '/dev/shm/test.db',
        'TEST_NAME': '/dev/shm/test.db',
    }
}


DATABASES = {
    'default':{
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': ':memory:',
        'TEST_NAME': ':memory:',
    }
}

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'),
    }
}

each seems to create a physical test.db file, which I don't understand, because unittests should be run in-memory. It should never save anything to disk. Presumably, it's failing to run syncdb after creating the file but before it executes the actual unittest. How do I fix this?

Edit: I discovered that, in one of my forms, I was populating field choices by directly querying a model (whereas I should have been doing that inside the form's init), so when Django's test framework imported my model, it was trying to read the table before the sqlite3 database had been created. I fixed that, but now I'm getting the error:

DatabaseError: table "myapp_mythroughmodel" already exists

so I'm back to square one, even though it's throwing a different exception type than initially.

Edit: I had a duplicate through model defined, causing Django to attempt to create it twice, resulting in the error.

4

2 回答 2

3

这也发生在我的遗留代码上,但出于另一个原因。

我有两个模型,其中 db_table 引用了同一个数据库表。我知道那很愚蠢,但这不是我的错)

而且我从来没有在互联网上找到任何可以帮助我的东西。我被设置为 3 的详细程度(manage.py test -v 3)保存了希望这对任何人都有帮助。

class Bla1(Model):
    some_column = ...
    class Meta:
        db_table = 'some_table'

class Bla2(Model):
    some_column = ...
    class Meta:
        db_table = 'some_table'
于 2014-08-15T13:37:21.490 回答
2

这个错误是由几个问题造成的。我将在这里总结它们以帮助其他可能偶然发现这一点的人。

  1. 确保您的 settings.DATABASES 设置正确。Django 的文档提到 using ,但为了清楚起见,我发现检查命令并覆盖所有内容TEST_NAME更容易。test例如,在我的 settings.py 的底部,我有:

    if 'test' in sys.argv:
        DATABASES = {
            'default':{
                'ENGINE': 'django.db.backends.sqlite3',
                'NAME': ':memory:',
            },
        }
    

    除非您有充分的理由,否则请始终使用:memory:以确保它在内存中运行并且不会创建将陷入磁盘的物理文件。出于某种奇怪的原因,SO 上的许多其他答案建议指定 test.db 文件的文字路径以进行测试。这是一个可怕的建议。

  2. 除非您想测试 South 和/或 South 迁移,否则请禁用 South,因为它只会使事情复杂化:

    SOUTH_TESTS_MIGRATE = False
    SKIP_SOUTH_TESTS = True
    
  3. 不要像我一样愚蠢并尝试在创建模型之前访问它们。这主要意味着不要直接引用其他模型或表单领域的模型。例如

    class MyForm(forms.Form):
    
        somefield = forms.ChoiceField(
            required=True,
            choices=[(_.id, _.name) for _ in OtherModel.objects.filter(criteria=blah)],
        )
    

    这可能在您的数据库已经存在的代码中起作用,但是当它尝试加载您的测试时,它会破坏 Django 的 unittest 框架,该测试会加载您的 models.py 和 forms.py,导致它读取一个不存在的表。相反,在表单的__init__().

于 2014-05-01T16:23:08.693 回答