Multiple Database Setup problem !?

131 views
Skip to first unread message

David Lubomirov

unread,
Oct 30, 2018, 6:09:16 AM10/30/18
to Django users
Hello,

In my project I have 3 applications, and I'm trying to split them across 2 databases.

More specifically 2 of the apps and Django "auth" application should work with the first database,
and the last application should remain in the second database.

These are the DB settings:

DATABASE_ROUTERS = [
    '<PATH_TO_ROUTER>.PrimaryRouter',
    '<PATH_TO_ROUTER>.ValidationRouter'
]

DATABASES = {
    'default': {},
    'primary': {
        'NAME': 'primary',
        'ENGINE': 'django.db.backends.postgresql',
        'USER': '<DB_USER>',
        'PASSWORD': '<DB_PASS>',
        'HOST': 'localhost',
    },
    'validations': {
        'NAME': 'validations',
        'ENGINE': 'django.db.backends.postgresql',
        'USER': '<DB_USER>',
        'PASSWORD': '<DB_PASS>',
        'HOST': 'localhost',
    }

Therefore these are the class routers:

class PrimaryRouter:
    """
    Router to control all database operations for the following applications:
    - first_model
    - second_model
    - auth
    """
    def db_for_read(self, model, **hints):
        if model._meta.app_label == 'auth' or \
            model._meta.app_label == 'first_model' or \
            model._meta.app_label == 'second_model':

            return 'primary'

        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label == 'auth' or \
            model._meta.app_label == 'first_model' or \
            model._meta.app_label == 'second_model':

            return 'primary'

        return None

    def allow_relation(self, first_object, second_object, **hints):
        if first_object._meta.app_label == 'auth' or \
            second_object._meta.app_label == 'auth':
            
            return True
        
        if first_object._meta.app_label == 'first_model' or \
            second_object._meta.app_label == 'first_model':

            return True

        if first_object._meta.app_label == 'second_model' or \
            second_object._meta.app_label == 'second_model':

            return True

        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if app_label == 'auth' or \
            app_label == 'first_model' or \
            app_label == 'second_model':

            return db == 'primary'

        return None


class ValidationRouter:
    """
    Router to control all database operations for the following applications:
    - fourth_model
    """
    def db_for_read(self, model, **hints):
        if model._meta.app_label == 'fourth_model':
            return 'validations'

        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label == 'fourth_model':
            return 'validations'

        return None

    def allow_relation(self, first_object, second_object, **hints):
        if first_object._meta.app_label == 'fourth_model' or \
            second_object._meta.app_label == 'fourth_model':
            return True

        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if app_label == 'fourth_model':
            return True
        
        return None


When I run migration on the first database "primary", everything is working.
But on the second database I'm getting the following exception:


django.db.utils.ProgrammingError: relation "auth_user" does not exist


The question - do I need to also specify the "auth" Django application in the second DB router "validations",
or there's something wrong with my setup ?

According to Django docs for version 2.1, this kind of setup is working - https://docs.djangoproject.com/en/2.1/topics/db/multi-db/ .

Tom Evans

unread,
Nov 2, 2018, 9:14:49 PM11/2/18
to django...@googlegroups.com
Hi David

Your router isn't configured correctly. This applies to all the
allow_foo() methods, but see allow_migrate [1] as an example:

Determine if the migration operation is allowed to run on the
database with alias db. Return True if the operation should run, False
if it shouldn’t run, or None if the router has no opinion.

Your routers *should* be having an opinion about whether that
app/model can be migrated on a specific database! For instance, you
say "the last app" should stay only on validations, but what your
ValidationRouter says is "If the app label is called fourth_model, run
migrations on this database", but it never checks what the database
is.

For ValidationRouter.allow_migrate you probably want something like this:

def allow_migrate(self, ...):
if app_label == "fourth_model":
return db == "validations"
elif db == "validations":
return False

EG:
* if the app label is an app that should be in the validations DB,
allow migrate when the db is the validations DB
* if it isn't and the DB is the validations DB, don't allow migrations to it
* if neither of those things, this router doesn't care.

Similarly, for allow_relations() you should be returning False when
the models should not be related.

Incidentally, all python functions return None if the end of the
function is reached without an explicit return value, so you never
have to end your functions with an explicit "return None".

Cheers

Tom

[1] https://docs.djangoproject.com/en/2.1/topics/db/multi-db/#allow_migrate
> --
> You received this message because you are subscribed to the Google Groups "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
> To post to this group, send email to django...@googlegroups.com.
> Visit this group at https://groups.google.com/group/django-users.
> To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/8eef7cfe-93ed-4322-ab84-09261e27f460%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages