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

问题描述 投票:29回答:7

我有一个托管= 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”?

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

python django unit-testing
7个回答
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

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 ...")

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。


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)

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()

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

1
投票

您可以在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())