How to create table during Django tests with managed = False

前端 未结 9 1867
执笔经年
执笔经年 2020-12-24 12:25

I have a model with managed = False.

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


        
相关标签:
9条回答
  • 2020-12-24 12:44

    You can use SchemaEditor in TestCase.setUp method to explicitly create models with 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'
    

    And in your tests:

    # 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())
    
    0 讨论(0)
  • 2020-12-24 12:47

    Execute raw SQL to create the table in the test setup:

    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 ...")
    
    0 讨论(0)
  • 2020-12-24 12:49

    Simply move your unmanaged models to a dedicated app and delete migrations folder. Details in my answer in Multi db and unmanged models - Test case are failing

    0 讨论(0)
  • 2020-12-24 12:49

    Using pytest and pytest-django

    To make this work (has been tested with django 3.0.2, pytest 5.3.5 and pytest-django 3.8.0):

    1. Run your pytest with the additional argument --no-migrations.
    2. Put the following code in your conftest.py. This has an unfortunate amount of copypasta, but I could not figure out how to first make my models unmanaged and then call the original django_db_setup. The issue of not being able to call pytest fixtures directly is discussed here: 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)
    
    0 讨论(0)
  • 2020-12-24 12:56

    Create your own test runner using this:

    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
    

    Then on your settings add this class to TEST_RUNNER.

    0 讨论(0)
  • 2020-12-24 12:59

    Nice plug and play solution. Just paste this before your test class definition. (note: django 1.8 used)

    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()
    
    0 讨论(0)
提交回复
热议问题