Multi db и unmanged модели - тестовый случай не удался - PullRequest
1 голос
/ 30 сентября 2019

У меня есть настройка mutlidb с неуправляемыми (только для чтения) моделями. Там нет миграции для этих моделей. Я пытался проверить функциональность view.py. В базе данных sqlite3 эти схемы тестовой таблицы не создают проблемы при неудачном тестировании. В view.py я импортировал неуправляемую (только для чтения) модель. Сбой.

Я перешел по ссылке для проверки Тестирование на неуправляемых моделях

Упоминается настройка Multi-db

test_runner.py

from django.test.runner import DiscoverRunner


class DisableMigrations(object):
    def __contains__(self, item):
        return True

    def __getitem__(self, item):
        return None


class UnManagedModelTestRunner(DiscoverRunner):

    def setup_test_environment(self, *args, **kwargs):
        from django.apps import apps
        # app_name = apps.get_app_config('core_esp')
        self.unmanaged_models = [m for m in apps.get_models() if not m._meta.managed and m._meta.app_label is 'core_esp']
        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 un managed models
        for m in self.unmanaged_models:
            m._meta.managed = False

settings.py

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

INSTALLED_APPS = ['test']
# Skip the migrations by setting "MIGRATION_MODULES"
# to the DisableMigrations class defined above
#
MIGRATION_MODULES = DisableMigrations()

# Set Django's test runner to the custom class defined above
TEST_RUNNER = 'apps.settings.test_runner.UnManagedModelTestRunner'
DATABASE_ROUTERS = ('apps.tests.test_dbrouter.TestApiRouter', )

models.py

from django.db import models

class TestModelA(models.Model):
    testid = models.CharField(max_length=200)
    class Meta:
        managed = False
        db_table = 'TestD'

class TestModelB(models.Model):
    testid = models.CharField(max_length=200)
    class Meta:
        managed = False
        db_table = 'test_model_b'
        app_label = 'application_b'

test.py

class MyTestCase(TestCase):

    def test_my_function(self):
       # view is called
       # serializer called with read only model
       pass

Ошибка

Traceback (most recent call last):                                                                                 
File "C:\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
return self.cursor.execute(sql, params)                                                                                                                                   
File "C:\lib\site-packages\django\db\backends\sqlite3\base.py", line 383, in execute return Database.Cursor.execute(self, query, params)                                                                                                            
sqlite3.OperationalError: no such table: TestD 
The above exception was the direct cause of the following exception:                                                                                                                                              Traceback (most recent call last): 
File "manage.py", line 21, in <module> main() 
File "manage.py", line 17, in main execute_from_command_line(sys.argv)
File "C:\lib\site-packages\django\core\management\__init__.py", line 381, in execute_from_command_line utility.execute()
File "C:\lib\site-packages\django\core\management\__init__.py", line 375, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "C:\lib\site-packages\django\core\management\commands\test.py", line 23, in run_from_argv
super().run_from_argv(argv)
File "C:\lib\site-packages\django\core\management\base.py", line 323, in run_from_argv
self.execute(*args, **cmd_options)
File "C:\lib\site-packages\django\core\management\base.py", line execute
output = self.handle(*args, **options)
File "C:\lib\site-packages\django\core\management\commands\test.py", line 53, in handle failures = test_runner.run_tests(test_labels)
File "C:\lib\site-packages\django\test\runner.py", line 627, in run_tests  
suite = self.build_suite(test_labels, extra_tests)
File "C:\lib\site-packages\django\test\runner.py", line 488, in build_suites   
tests = self.test_loader.loadTestsFromName(label)                                                                                                    
File "c:\program files\python37\Lib\unittest\loader.py", line 154, in loadTestsFromName 
module = __import__(module_name)                                                                                                                                           
File "C:\tests\tests.py", line 5, in <module>       
from .views import (                                                                                                                             
File "C:\views.py", line 6, in <module>    
from .serializers import (                                                                                                                       
File "C:\serializers.py", line 9, in <module>   
class TestSerializer(serializers.Serializer):                                                                                                                           
File "C:\serializers.py", line 13, in TestSerializer     
required=False, style={'base_template': 'select_multiple.html'}                                                                                                           
File "C:\lib\site-packages\rest_framework\fields.py", line 1476, in __init__                                                                      
super(MultipleChoiceField, self).__init__(*args, **kwargs)                                                                                                              
File "C:\lib\site-packages\rest_framework\fields.py", line 1417, in __init__                                                                         
self.choices = choices                                                                                                                        
File "C:\lib\site-packages\rest_framework\fields.py", line 1453, in _set_choices                                                                                                                                                                                                                         
for row in compiler.results_iter(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size):  
File "C:\lib\site-packages\django\db\models\sql\compiler.py", line 1052, in results_iter                                          
results = self.execute_sql(MULTI, chunked_fetch=chunked_fetch, chunk_size=chunk_size)                                       
File "C:\lib\site-packages\django\db\models\sql\compiler.py", line 1100, in execute_sql                                                             
cursor.execute(sql, params)                                                                                
File "C:\lib\site-packages\django\db\backends\utils.py", line 67, in execute 
return self._execute_with_wrappers(sql, params, many=False, 
executor=self._execute)
File "C:\lib\site-packages\django\db\backends\utils.py", line 76, in 
_execute_with_wrappers                                               
return executor(sql, params, many, context)                                                                                                                               
File "C:\lib\site-packages\django\db\backends\utils.py", line 84, in _execute                                                   
return self.cursor.execute(sql, params)                                                                                                                                   
File "C:\lib\site-packages\django\db\utils.py", line 89, in __exit__  
raise dj_exc_value.with_traceback(traceback) from exc_value           
File "C:\lib\site-packages\django\db\backends\utils.py", line 84, in _execute                                                                  
return self.cursor.execute(sql, params)                                                                                                                                   
File "C:\lib\site-packages\django\db\backends\sqlite3\base.py", line 383, in execute                                                                
return Database.Cursor.execute(self, query, params)                                                                                                                     
django.db.utils.OperationalError: no such table: TESTD

Ответы [ 2 ]

0 голосов
/ 17 октября 2019

Я нашел хорошее решение, работающее в текущей версии 2.2. Прекрасно работает с pytest-django, но, вероятно, будет работать даже без pytest.

Чтобы это работало, вам необходимо:

  1. разделить ваш проект на два приложения django. Один будет содержать только неуправляемые модели, а другой будет содержать остальные. Не указывайте значение управляемого = Ложное в любом из этих двух приложений

  2. в файле settings.py в БАЗАХ ДАННЫХ: у вас будет две базы данных, одна по умолчанию и одна ваша внешняя БД

  3. В той же папке, где у вас есть настройки, создайте routers.py и внедрите маршрутизатор, который будет маршрутизировать на основе метки приложения к данной БД (приблизительно 45 строк, включая проверки того, что вы никогда не будете писать в эти внешние БД). Затем добавьте этот маршрутизатор в список ROUTERS файла settings.py.

При запуске тестов с pytest он действительно создает все таблицы для приложений, у которых нет папки миграции

0 голосов
/ 01 октября 2019

Я могу показать вам, как я решаю эту проблему;

Итак, я создал сигнал, который более или менее выглядит следующим образом:

def create_test_models(**kwargs):
    if "test" in sys.argv:
        Organization = apps.get_model("organizations.Organization")
        ...  # other models here too
        # as we do not manage it - we need to create it manually;
        with connection.schema_editor() as schema_editor:
            sid = transaction.savepoint()
            try:
                schema_editor.create_model(Organization)
                ... # different models here if needed
                transaction.savepoint_commit(sid)
            except ProgrammingError:  # tables already exists;
                transaction.savepoint_rollback(sid)

Этот сигнал подключен в конфигурации как pre_migrate сигнал:

class OrganizationsConfig(AppConfig):
    name = "engine.organizations"

    def ready(self):
        # run after each migration; so each deploy, but this method can handle the
        # incremental updates.
        pre_migrate.connect(create_test_models, sender=self)

Возможно, это не супер-решение, но оно работает, и у вас есть модели, созданные во время тестов, и вы можете играть с ними или создавать тестовые данные и т. Д.

Надеюсь, что это поможет вам двигаться вперед.

...