Simplifying the Testing of Unmanaged Database Models in Django

Sometimes, when building a web application in Django, one needs to connect to a legacy database whose tables already exist. To support this use case, Django has the concept of "unmanaged models," which let you connect the Django ORM to tables that it assumes to exist (and not attempt to create).

This can make automated testing---which is something we take seriously at Caktus---rather difficult, because you might not have the SQL on hand to create an empty copy of the legacy database for testing purposes. One solution is to automatically set all your unmanaged models to "managed" during a test run, so that Django will happily create the tables for you. Typically this is enough to allow you to add sample data to the database and write tests as you would for any other model in Django. We've also found the approach to work especially well for database views (which typically are manifested as unmanaged models in Django), because it may be easier to test the code that uses the view by treating it as a table during automated testing.

There's a great snippet available for doing this, but the code is lengthly and and basically requires copying and pasting a large portion of the existing test runner in Django. Django 1.2, however, introduces a new class-based test runner that's much better suited for small modifications to the testing process like this.

To give it a try, I wrote a short piece of code that accomplishes this---making all unmanaged models in your Django project "managed" for the duration of the test run:

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

Enjoy! Don't hesitate to comment with any questions or concerns.

New Call-to-action
blog comments powered by Disqus
Times
Check

Success!

Times

You're already subscribed

Times