2

I am having 3 databases defined in the Settings.py and routers for two apps in the respective app folders. When I try to run manage.py migrate --database="app_db" respectively for apps, it only runs default and second database list item. If I change the order of database list it won't perform the last one but only second one.

Settings.py

INSTALLED_APPS = [
    # django apps
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    # my apps
    "cred",
    "dms",
    # third party apps
    "rest_framework",
]


DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "databases/db.sqlite3",
    },
    "dms_db": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "databases/dms.sqlite3",
    },
    "cred_db": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "databases/cred.sqlite3",
    },
}

DATABASE_ROUTERS = [
    "dms.routers.DMSRouter",
    "cred.routers.CredRouter",
]

AUTH_USER_MODEL = "cred.User"
CRED_DB_ROUTER = "cred_db"
DMS_DB_ROUTER = "dms_db"

if I put dms_db at list index 1 then it will migrate but cred_db won't. If I put cred_db at index 1 then it will migrate but dms_db won't.

Cred.Router.py

from django.conf import settings


class CredRouter:
    route_app_labels = ["auth", "cred", "contenttypes"]

    def db_for_read(self, model, **hints):
        if model._meta.app_label in self.route_app_labels:
            return settings.CRED_DB_ROUTER
        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label in self.route_app_labels:
            return settings.CRED_DB_ROUTER
        return None

    def allow_relation(self, obj1, obj2, **hints):
        if (
            obj1._meta.app_label in self.route_app_labels
            or obj2._meta.app_label in self.route_app_labels
        ):
            return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if app_label in self.route_app_labels:
            return db == settings.CRED_DB_ROUTER
        return False

dms.router.py

from django.conf import settings


class DMSRouter:
    route_app_labels = [
        "dms",
    ]

    def db_for_read(self, model, **hints):
        if model._meta.app_label in self.route_app_labels:  # noqa
            return settings.DMS_DB_ROUTER
        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label in self.route_app_labels:  # noqa
            return settings.DMS_DB_ROUTER
        return None

    def allow_relation(self, obj1, obj2, **hints):
        if (
            obj1._meta.app_label in self.route_app_labels  # noqa
            or obj2._meta.app_label in self.route_app_labels  # noqa
        ):
            return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if app_label in self.route_app_labels:
            return db == settings.DMS_DB_ROUTER
        return False

What I am running

Only initial_0001.py is there in migrations folder. So I guess? it is not making some issue. Not sure tho.

manage.py makemigrations 
manage.py migrate
manage.py migrate --database="cred_db"
manage.py migrate --database="dms_db"

All the models share this structure exactly.

class Organization(models.Model):
    name = models.CharField(max_length=255)
    email = models.EmailField()
    codename = models.CharField(max_length=254, unique=True)
    ... 

    objects = models.Manager()

    class Meta:
        verbose_name = _("organization")
        verbose_name_plural = _("organization")

As the project evolves we will be having more databases connections as we will be integrating our existing Django apps and each has a dedicated databases server already established. So this project will be connecting to at min 8-10 different databases. Manually migrating each by moving will be a issue for so many of them.

1 Answer 1

2

A Router works for all the databases, hence allow_migrate in DMSRouter is called for both dms_db and cred_db. Since DMSRouter only allows tables to be migrated to only dms app then your other apps tables are never migrated. What you need to do is use the argument db passed in the allow_migrate method and if it is not the one the router is dealing with then return None instead of False:

Cred.Router.py:

class CredRouter:
    route_app_labels = ["auth", "cred", "contenttypes"]

    def db_for_read(self, model, **hints):
        if model._meta.app_label in self.route_app_labels:
            return settings.CRED_DB_ROUTER
        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label in self.route_app_labels:
            return settings.CRED_DB_ROUTER
        return None

    def allow_relation(self, obj1, obj2, **hints):
        if (
            obj1._meta.app_label in self.route_app_labels
            or obj2._meta.app_label in self.route_app_labels
        ):
            return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if app_label in self.route_app_labels:
            return db == settings.CRED_DB_ROUTER
        else if db == settings.CRED_DB_ROUTER:
            return False # Only apps in `route_app_labels` will be migrated to `cred_db`
        return None # Let other routers decide

dms.router.py:

from django.conf import settings


class DMSRouter:
    route_app_labels = [
        "dms",
    ]

    def db_for_read(self, model, **hints):
        if model._meta.app_label in self.route_app_labels:  # noqa
            return settings.DMS_DB_ROUTER
        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label in self.route_app_labels:  # noqa
            return settings.DMS_DB_ROUTER
        return None

    def allow_relation(self, obj1, obj2, **hints):
        if (
            obj1._meta.app_label in self.route_app_labels  # noqa
            or obj2._meta.app_label in self.route_app_labels  # noqa
        ):
            return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if app_label in self.route_app_labels:
            return db == settings.DMS_DB_ROUTER
        else if db == settings.DMS_DB_ROUTER:
            return False # Only apps in `route_app_labels` will be migrated to `dms_db`
        return None # Let other routers decide
Sign up to request clarification or add additional context in comments.

1 Comment

I was hoping for you to answer, searched for some twitter handle so I could DM you the question but couldn't find one. Never mind. Thank you so much.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.