Testing with the legacy database in Django

Thuc Pham
An Idea (by Ingenious Piece)
3 min readAug 9, 2020

Introduction

In Django, to work with the legacy database, you must set the manged=Falsein the ORM model Meta class to Django know that you don’t want to create, modify or delete the table. For example, the Person class below tells Django that it already has a table called ‘APP_PERSON’ in the database.

class Person(models.Model):
id = models.IntegerField(primary_key=True)
first_name = models.CharField(max_length=70)
class Meta:
managed = False
db_table = 'APP_PERSON'

However, it becomes a challenge for unit testing, because Django automatically generates the testing database (blank database) which we may not have the SQL script to create it. Therefore, running the unit testing $python manage.py test will throw an exception django.db.utils.OperationalError: no such table:<TB_NAME>

I have found several approaches to solve this problem. One of them is the solution of Tobias McNulty [2] which require you to create a custom test runner. The idea is to modify the flag of the unmanaged models to True managed=True while running the test and revert it back to False after finishing the testing. However, the DjangoTestSuiteRunner has been replaced by DiscoverRunner since Django 1.8, and thus you must change the code a little bit to work with the latest version of Django.

Customise Test Runner

Firstly, you create the test runner class in the test_runner.py file. Here is the test runner code snippet

from django.test.runner import DiscoverRunnerclass ManagedModelTestRunner(DiscoverRunner):
"""
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):
#2
from django.apps import apps
get_models = apps.get_models
self.unmanaged_models = [m for m in get_models() if not m._meta.managed] #3
for m in self.unmanaged_models:
m._meta.managed = True # 4
super(ManagedModelTestRunner, self).setup_test_environment(*args, **kwargs)
def teardown_test_environment(self, *args, **kwargs):
super(ManagedModelTestRunner, self).teardown_test_environment(*args, **kwargs)
# 5:
for m in self.unmanaged_models:
m._meta.managed = False
  1. Import the test runner
  2. Setup the environment
  3. Get all unmanaged models in the projects
  4. Set the flag to True
  5. Revert the meta back to False after finishing the testing

Usage

To use the test runner, you must add the following in the settings file that you are using for testing purposes.

TEST_RUNNER = '<PROJECT_NAME>.test_runner.ManagedModelTestRunner'

Before running the test, you should run the migrate command to make sure that all necessary admin permissions or any content types have been installed.

If you want to run the test without running the migration, you can install django-test-without-migrations to skip this phase.

Install django-test-without-migrations version 0.6

This package works fine in Django v2.2. To install the package, you can run the following command.

$ pip install django-test-without-migrations

After that, add it to your INSTALLED_APP in the settings.py

INSTALLED_APPS = (
# ...
'test_without_migrations',
)

Testing

Now let run the test with the --nomigrations or -n for short.

$ python manage.py test -n

This is done for this topic. Enjoy your coding. If you have any queries, feel free to contact me. I am happy to discuss and connect with you.

Twitter: https://twitter.com/steve_pham93

Email: duythuc28493@gmail.com

Reference

[1] https://stackoverflow.com/a/5547073/4166366

[2] https://www.caktusgroup.com/blog/2010/09/24/simplifying-the-testing-of-unmanaged-database-models-in-django/

[3] https://djangosnippets.org/snippets/1596/

[4] https://dev.to/vergeev/testing-against-unmanaged-models-in-django

--

--