I am setting up a Django application with a lot of databases, and some of them are using the same models (they are not replicas). I already configured my routers and everything is working great. The problem appears when making the tests as I want to use factory-boy
.
On a different project I could setup the database inside Meta
but now, I have to select on which database to create an instance dynamically (if not, I would have to create a DjangoModelFactory
for each database, which wouldn't be pretty).
Is there an (easier) way to specify the database dynamically for each creation?
As far as I know factory_boy
(version <=2.10.0
) doesn't provide anything like that.
Though, your problem is the perfect use case to use a context manager. It will allow you to set the database dynamically wherever you need and only under the desired scope, and also DRY!:
# factoryboy_utils.py
@classmethod
def _get_manager(cls, model_class):
return super(cls, cls)._get_manager(model_class).using(cls.database)
class DBAwareFactory(object):
"""
Context manager to make model factories db aware
Usage:
with DBAwareFactory(PersonFactory, 'db_qa') as personfactory_on_qa:
person_on_qa = personfactory_on_qa()
...
"""
def __init__(self, cls, db):
# Take a copy of the original cls
self.original_cls = cls
# Patch with needed bits for dynamic db support
setattr(cls, 'database', db)
setattr(cls, '_get_manager', _get_manager)
# save the patched class
self.patched_cls = cls
def __enter__(self):
return self.patched_cls
def __exit__(self, type, value, traceback):
return self.original_cls
and then, in your tests, you can do something like:
from factoryboy_utils import DBAwareFactory
class MyTest(TestCase):
def test_mymodel_on_db1(self):
...
with DBAwareFactory(MyModelFactory, 'db1') as MyModelFactoryForDB1:
mymodelinstance_on_db1 = MyModelFactoryForDB1()
# whatever you need with that model instance
...
# something else here
def test_mymodel_on_db2(self):
...
with DBAwareFactory(MyModelFactory, 'db2') as MyModelFactoryForDB2:
mymodelinstance_on_db2 = MyModelFactoryForDB2()
# whatever you need with that model instance
...
# something else here