From 23511d04b2ba42bfb23fb813ae9a70d4b4ea9d04 Mon Sep 17 00:00:00 2001 From: Vasanth Date: Sat, 26 Nov 2022 09:53:33 +0100 Subject: [PATCH] Fix #208 - do not assume active DB when not specified (#210). * Do not assume active DB when not specified * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add db routing test case * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * one more test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * use assertRaises instead of expectedFailure * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Catch a general exception for django < 2.0 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * use `with` instead of callable * skip test for django < 2.0 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove repeated test Co-authored-by: vaz Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- admin_interface/models.py | 6 ++++-- tests/routers.py | 45 +++++++++++++++++++++++++++++++++++++++ tests/settings.py | 3 +++ tests/test_multidb.py | 40 ++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 tests/routers.py create mode 100644 tests/test_multidb.py diff --git a/admin_interface/models.py b/admin_interface/models.py index 2f6ec7a..980808b 100644 --- a/admin_interface/models.py +++ b/admin_interface/models.py @@ -45,8 +45,10 @@ class Theme(models.Model): pass @staticmethod - def get_active_theme(database="default"): - objs_manager = Theme.objects.using(database) + def get_active_theme(database=None): + objs_manager = ( + Theme.objects if database is None else Theme.objects.using(database) + ) objs_active_qs = objs_manager.filter(active=True) objs_active_ls = list(objs_active_qs) objs_active_count = len(objs_active_ls) diff --git a/tests/routers.py b/tests/routers.py new file mode 100644 index 0000000..682a8b6 --- /dev/null +++ b/tests/routers.py @@ -0,0 +1,45 @@ +DATABASE_APPS_MAPPING = { + "admin_interface": "default", +} + + +class DatabaseAppsRouter(object): + """ + arouter to control all database operations on models for different + databases. + + in case an app is not set in DATABASE_APPS_MAPPING, the router + will fallback to the `default` database. + + Settings example: + + DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'} + """ + + def __init__(self, db_map=DATABASE_APPS_MAPPING): + """ + If routers is not specified, default to DATABASE_APPS_MAPPING + """ + self.db_map = db_map + + def db_for_read(self, model, **hints): + """Point all read operations to the specific database""" + if model._meta.app_label in self.db_map: + return self.db_map[model._meta.app_label] + + return None + + def db_for_write(self, model, **hints): + """Point all write operations to the specific database""" + if model._meta.app_label in self.db_map: + return self.db_map[model._meta.app_label] + + return None + + def allow_relation(self, obj1, obj2, **hints): + """Allow any relation between apps that use the same database""" + return None + + def allow_migrate(self, db, app_label, model_name=None, **hints): + """Make sure that apps only appear in the related database""" + return None diff --git a/tests/settings.py b/tests/settings.py index 2e63bb4..d98896e 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -117,6 +117,9 @@ DATABASES = { "replica": database_config.get(replica_engine), } + +DATABASE_ROUTERS = ["tests.routers.DatabaseAppsRouter"] + USE_I18N = True LANGUAGES = ( ("en", "English"), diff --git a/tests/test_multidb.py b/tests/test_multidb.py new file mode 100644 index 0000000..83915f1 --- /dev/null +++ b/tests/test_multidb.py @@ -0,0 +1,40 @@ +from unittest import skipIf + +from django import VERSION +from django.test import TestCase + +from admin_interface.models import Theme + +from .routers import DatabaseAppsRouter + + +class AdminInterfaceModelsWithDBRoutingTestCase(TestCase): + databases = ["replica"] + + def test_standard_dbrouter(self): + router = DatabaseAppsRouter() + db_for_theme = router.db_for_read(Theme) + assert db_for_theme == "default" + + def test_dbrouter_selects_correct_db(self): + DATABASE_APPS_MAPPING = { + "admin_interface": "replica", + } + router = DatabaseAppsRouter(db_map=DATABASE_APPS_MAPPING) + db_for_theme = router.db_for_read(Theme) + assert db_for_theme == "replica" + + @skipIf( + VERSION[0] < 2, "TestCase does not respect database param on older versions" + ) + def test_dbrouter_errors_when_fetching_from_default(self): + with self.assertRaises(Exception): + Theme.get_active_theme() + + def test_dbrouter_fetches_db(self): + DATABASE_APPS_MAPPING = { + "admin_interface": "replica", + } + router = DatabaseAppsRouter(db_map=DATABASE_APPS_MAPPING) + with self.settings(DATABASE_ROUTERS=[router]): + Theme.get_active_theme()