如何在使用managed = False的Django测试期间创建表

时间:2011-08-11 04:50:45

标签: python django unit-testing

我有一个托管= False的模型。

class SampleModel(models.Model):
    apple = models.CharField(max_length=30)
    orange = models.CharField(max_length=30)

    class Meta:
        managed = False

我有一个单元测试,它创建了一个SampleModel,但是当我运行测试时,我得到了:

DatabaseError: no such table: SAMPLE_SAMPLE_MODEL

django docs - https://docs.djangoproject.com/en/dev/ref/models/options/#managed记录了以下内容:

  

对于涉及托管= False的模型的测试,由您决定   确保在测试设置中创建正确的表格。

如何在测试设置期间实际“创建”表格?或者,我怎样才能使它在运行测试时,在测试期间该模型具有“managed = True”?

在实际应用程序中,此模型实际上由数据库中的视图支持。但是对于测试期间,我想将其视为一个表,并能够在那里插入测试数据。

9 个答案:

答案 0 :(得分:14)

查看此博文:http://www.caktusgroup.com/blog/2010/09/24/simplifying-the-testing-of-unmanaged-database-models-in-django/它详细描述了为非托管模型创建测试运行器。

from django.test.simple import DjangoTestSuiteRunner


class ManagedModelTestRunner(DjangoTestSuiteRunner):
    """
    Test runner that automatically makes all unmanaged models in your Django
    project managed for the duration of the test run, so that one doesn't need
    to execute the SQL manually to create them.
    """
    def setup_test_environment(self, *args, **kwargs):
        from django.db.models.loading import get_models
        self.unmanaged_models = [m for m in get_models()
                                 if not m._meta.managed]
        for m in self.unmanaged_models:
            m._meta.managed = True
        super(ManagedModelTestRunner, self).setup_test_environment(*args,
                                                                   **kwargs)

    def teardown_test_environment(self, *args, **kwargs):
        super(ManagedModelTestRunner, self).teardown_test_environment(*args,
                                                                      **kwargs)
        # reset unmanaged models
        for m in self.unmanaged_models:
            m._meta.managed = False

答案 1 :(得分:4)

执行原始SQL以在测试设置中创建表:

from django.db import connection

class MyTest(unittest.TestCase):
    def setUp(self):
        connection.cursor().execute("CREATE TABLE ...")

    def tearDown(self):
        connection.cursor().execute("DROP TABLE ...")

答案 2 :(得分:3)

使用以下方法创建自己的测试运行器:

from django.test.simple import DjangoTestSuiteRunner

class NoDbTestRunner(DjangoTestSuiteRunner):
  """ A test runner to test without database creation """

  def setup_databases(self, **kwargs):
    """ Override the database creation defined in parent class """
    #set manage=True for that specific database on here

然后在您的设置上将此类添加到TEST_RUNNER。

答案 3 :(得分:2)

如果您没有许多非托管表,请快速解决问题:

首先在设置中添加一个新变量。

# settings.py
import sys
UNDER_TEST = (len(sys.argv) > 1 and sys.argv[1] == 'test')

然后在模型中

# models.py
from django.conf import settings

class SampleModel(models.Model):
    apple = models.CharField(max_length=30)
    orange = models.CharField(max_length=30)

    class Meta:
        managed = getattr(settings, 'UNDER_TEST', False)

答案 4 :(得分:2)

不错的即插即用解决方案。只需在测试类定义之前粘贴它即可。 (注意:使用django 1.8)

from django.db.models.loading import get_models

def change_managed_settings_just_for_tests():
  """django model managed bit needs to be switched for tests."""    

  unmanaged_models = [m for m in get_models() if not m._meta.managed]
  for m in unmanaged_models:
    m._meta.managed = True

change_managed_settings_just_for_tests()

答案 5 :(得分:2)

添加:django.db.models.loading.get_models将在Django 1.9中删除(参见https://github.com/BertrandBordage/django-cachalot/issues/33)。

以下是Django 1.10的更新版本:

class UnManagedModelTestRunner(DiscoverRunner):
    '''
    Test runner that automatically makes all unmanaged models in your Django
    project managed for the duration of the test run.
    Many thanks to the Caktus Group 
    '''

    def setup_test_environment(self, *args, **kwargs):
        from django.apps  import apps
        self.unmanaged_models = [m for m in apps.get_models() if not m._meta.managed]
        for m in self.unmanaged_models:
            m._meta.managed = True
        super(UnManagedModelTestRunner, self).setup_test_environment(*args, **kwargs)

    def teardown_test_environment(self, *args, **kwargs):
        super(UnManagedModelTestRunner, self).teardown_test_environment(*args, **kwargs)
        # reset unmanaged models
        for m in self.unmanaged_models:
            m._meta.managed = False 

请注意,您还需要注意迁移(请参阅Testing django application with several legacy databases

MIGRATION_MODULES = {
    'news': 'news.test_migrations',
    'economist': 'economist.test_migrations'
}

答案 6 :(得分:2)

您可以SchemaEditor方法中的TestCase.setUp使用managed = False明确创建模型。

# models.py

from django.db import models


class Unmanaged(models.Model):
    foo = models.TextField()

    class Meta:
        # This model is not managed by Django
        managed = False
        db_table = 'unmanaged_table'

在你的测试中:

# tests.py

from django.db import connection
from django.test import TestCase

from myapp.models import Unmanaged


class ModelsTestCase(TestCase):
    def setUp(self):
        super().setUp()

        with connection.schema_editor() as schema_editor:
            schema_editor.create_model(Unmanaged)

            if Unmanaged._meta.db_table not in connection.introspection.table_names():
                raise ValueError("Table `{table_name}` is missing in test database.".format(table_name=Unmanaged._meta.db_table))

    def tearDown(self):
        super().tearDown()

        with connection.schema_editor() as schema_editor:
            schema_editor.delete_model(Unmanaged)

    def test_unmanaged_model(self):
        with self.assertNumQueries(num=3):
            self.assertEqual(0, Unmanaged.objects.all().count())
            Unmanaged.objects.create()
            self.assertEqual(1, Unmanaged.objects.all().count())

答案 7 :(得分:0)

只需将非托管模型移动到专用应用程序并删除迁移文件夹。 Multi db and unmanged models - Test case are failing

中我的答案的详细信息

答案 8 :(得分:0)

使用pytest和pytest-django

要使其正常工作(已通过 django 3.0.2 pytest 5.3.5 pytest-django 3.8.0 进行了测试) :

  1. 使用附加参数--no-migrations运行pytest。
  2. 将以下代码放入conftest.py中。不幸的是,这有大量的copypasta,但是我不知道如何首先使模型不受管理,然后再调用原始的django_db_setup。这里讨论了无法直接调用pytest固定装置的问题:https://github.com/pytest-dev/pytest/issues/3950

conftest.py

# example file
import pytest
from pytest_django.fixtures import _disable_native_migrations


@pytest.fixture(scope="session")
def django_db_setup(
        request,
        django_test_environment,
        django_db_blocker,
        django_db_use_migrations,
        django_db_keepdb,
        django_db_createdb,
        django_db_modify_db_settings,
):
    # make unmanaged models managed
    from django.apps import apps
    unmanaged_models = []
    for app in apps.get_app_configs():
        unmanaged_models = [m for m in app.get_models()
                            if not m._meta.managed]
    for m in unmanaged_models:
        m._meta.managed = True

    # copypasta fixture code

    """Top level fixture to ensure test databases are available"""
    from pytest_django.compat import setup_databases, teardown_databases

    setup_databases_args = {}

    if not django_db_use_migrations:
        _disable_native_migrations()

    if django_db_keepdb and not django_db_createdb:
        setup_databases_args["keepdb"] = True

    with django_db_blocker.unblock():
        db_cfg = setup_databases(
            verbosity=request.config.option.verbose,
            interactive=False,
            **setup_databases_args
        )

    def teardown_database():
        with django_db_blocker.unblock():
            try:
                teardown_databases(db_cfg, verbosity=request.config.option.verbose)
            except Exception as exc:
                request.node.warn(
                    pytest.PytestWarning(
                        "Error when trying to teardown test databases: %r" % exc
                    )
                )

    if not django_db_keepdb:
        request.addfinalizer(teardown_database)