Freddy
Freddy

Reputation: 1

Django Routers with multiples schema in database

I have one database with multitples schemas, and I try to use routers to manage record informations. (https://www.amvtek.com/blog/posts/2014/Jun/13/accessing-multiple-postgres-schemas-from-django/)

In my django settings:

# myproject/myproject/settings.py
...

DATABASE_ROUTERS = [
    'routers.sharingRouter.SharingRouter',
]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'OPTIONS': {
            'options': '-c search_path=django'
        },
        'NAME': 'myproject',
        'USER': 'user1',
        'PASSWORD': 'pwd',
        'HOST': '127.0.0.1',
        'PORT': '5433',
        'CHARSET': 'UTF8',
    },
    'sharing_writers': {
        'ENGINE': 'django.db.backends.postgresql',
        'OPTIONS': {
            'options': '-c search_path=sharing'
        },
        'NAME': 'myproject',
        'USER': 'user2',
        'PASSWORD': 'pwd',
        'HOST': '127.0.0.1',
        'PORT': '5433',
        'CHARSET': 'UTF8',
    },
'sharing_readers': {
        'ENGINE': 'django.db.backends.postgresql',
        'OPTIONS': {
            'options': '-c search_path=sharing'
        },
        'NAME': 'myproject',
        'USER': 'user3',
        'PASSWORD': 'pwd',
        'HOST': '127.0.0.1',
        'PORT': '5433',
        'CHARSET': 'UTF8',
    },
}
...

In my router: (like in official documentation: https://docs.djangoproject.com/en/3.1/topics/db/multi-db/)

# myproject/routers/sharingRouter.py



class SharingRouter(object):
    """.

    A router to control all database operations on models in the
    auth and contenttypes applications.
    """

    route_app_labels = {
        'sharing',
    }

    def db_for_read(self, model, **hints):
        """.

        Attempts to read sharing models go to sharing_readers.
        """
        if model._meta.app_label in self.route_app_labels:
            return 'sharing_readers'
        return None

    def db_for_write(self, model, **hints):
        """.

        Attempts to write sharing model go to sharing_writers.
        """
        if model._meta.app_label in self.route_app_labels:
            return 'sharing_writers'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """.

        Allow relations if a model in the sharing app is
        involved.
        """
        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):
        """.

        Make sure the sharing app only appear in the
        'sharing_writers' database.
        """
        if app_label in self.route_app_labels:
            return db == 'sharing_writers'
        return None

In my app model:

# myproject/sharing/models.py
from django.db import models

class Gender(models.Model):
    """TODO."""

    name = models.CharField(
        max_length=50,
        unique=True,
        null=False,
        blank=False
    )

    class Meta():
        """TODO."""

        app_label = 'sharing'

I have more models, but it's just for example. You can see, app_label is defined.

When use migration command to record django data in first ./manage.py migrate result is correct:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK

Use default database, fine.

And in second time, I use makemigration and migrate for sharing app:

./manage.py makemigrations --name sharing sharing
Migrations for 'sharing':
  sharing/migrations/0001_sharing.py
    - Create model Gender
./manage.py migrate sharing
Operations to perform:
  Apply all migrations: sharing
Running migrations:
  Applying sharing.0001_sharing... OK

Is ok to, but not because sharing information use default database and not sharing_writers as if sharingRouter.py, and allow_migrate function, is not use. It's ok now, thanks @IainShelvington

I try to force it to add a defaultRouter, but not change. I try this too: Django multiple databases error with routes

===Editing===

I add a default router:

class DefaultRouter(object):
    """.

    A router to control all database operations on models in the
    auth and contenttypes applications.
    """

    route_app_labels = {
        'sharing',
    }


    def db_for_read(self, model, **hints):
        """.

        Attempts to read default django go to default.
        """
        if model._meta.app_label not in self.route_app_labels:
            return 'default'
        return None

    def db_for_write(self, model, **hints):
        """.

        Attempts to write default django go to default.
        """
        if model._meta.app_label not in self.route_app_labels:
            return 'default'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """.

        Allow relations if a model in the default app is
        involved.
        """
        if (
            obj1._meta.app_label not in self.route_app_labels or
            obj2._meta.app_label not in self.route_app_labels
        ):
            return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """.

        Make sure the sharing app only appear in the
        'default' database.
        """
        if app_label not in self.route_app_labels:
            print(f"default router - : {db} {app_label} {model_name} {hints} ")
            return db == 'default'
        return None

And I add a print on SharingRouter.allow_migrate:

if app_label in self.route_app_labels:
            print(f"sharing router - : {db} {app_label} {model_name} {hints} ")
            return db == 'sharing_writers'
        return None

I add new DefautRouter on settings, like this:

DATABASE_ROUTERS = [
    'routers.sharingRouter.SharingRouter',
    'routers.defaultRouter.DefaultRouter',
]

DefaultRouter in last.

I lauch migrate command without 'sharing' app in INSTALLED_APPS

./manage.py migrate
default router - : default admin logentry {} 
default router - : default admin logentry {} 
default router - : default admin logentry {} 
default router - : default admin logentry {} 
default router - : default admin logentry {} 
default router - : default admin logentry {} 
default router - : default admin logentry {} 
default router - : default admin logentry {} 
default router - : default admin logentry {'model': <class 'django.contrib.admin.models.LogEntry'>} 
default router - : default admin logentry {'model': <class 'django.contrib.admin.models.LogEntry'>} 
default router - : default admin logentry {'model': <class 'django.contrib.admin.models.LogEntry'>} 
default router - : default auth permission {} 
default router - : default auth permission {} 
default router - : default auth permission {} 
default router - : default auth permission {} 
default router - : default auth permission {'model': <class 'django.contrib.auth.models.Permission'>} 
default router - : default auth permission {'model': <class 'django.contrib.auth.models.Permission'>} 
default router - : default auth permission {'model': <class 'django.contrib.auth.models.Permission'>} 
default router - : default auth group {} 
default router - : default auth group {} 
default router - : default auth group {} 
default router - : default auth group {'model': <class 'django.contrib.auth.models.Group'>} 
default router - : default auth group {'model': <class 'django.contrib.auth.models.Group'>} 
default router - : default auth group {'model': <class 'django.contrib.auth.models.Group'>} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {'model': <class 'django.contrib.auth.models.User'>} 
default router - : default auth user {'model': <class 'django.contrib.auth.models.User'>} 
default router - : default auth user {'model': <class 'django.contrib.auth.models.User'>} 
default router - : default contenttypes contenttype {} 
default router - : default contenttypes contenttype {} 
default router - : default contenttypes contenttype {} 
default router - : default contenttypes contenttype {'model': <class 'django.contrib.contenttypes.models.ContentType'>} 
default router - : default contenttypes contenttype {'model': <class 'django.contrib.contenttypes.models.ContentType'>} 
default router - : default contenttypes contenttype {'model': <class 'django.contrib.contenttypes.models.ContentType'>} 
default router - : default sessions session {} 
default router - : default sessions session {} 
default router - : default sessions session {} 
default router - : default sessions session {'model': <class 'django.contrib.sessions.models.Session'>} 
default router - : default sessions session {'model': <class 'django.contrib.sessions.models.Session'>} 
default router - : default sessions session {'model': <class 'django.contrib.sessions.models.Session'>} 
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial...default router - : default contenttypes contenttype {'model': <class '__fake__.ContentType'>} 
default router - : default contenttypes contenttype {'model': <class '__fake__.ContentType'>} 
 OK
  Applying auth.0001_initial...default router - : default auth permission {'model': <class '__fake__.Permission'>} 
default router - : default auth group {'model': <class '__fake__.Group'>} 
default router - : default auth user {'model': <class '__fake__.User'>} 
 OK
  Applying admin.0001_initial...default router - : default admin logentry {'model': <class '__fake__.LogEntry'>} 
 OK
  Applying admin.0002_logentry_remove_auto_add...default router - : default admin logentry {'model': <class '__fake__.LogEntry'>} 
 OK
  Applying admin.0003_logentry_add_action_flag_choices...default router - : default admin logentry {'model': <class '__fake__.LogEntry'>} 
 OK
  Applying contenttypes.0002_remove_content_type_name...default router - : default contenttypes contenttype {'model': <class '__fake__.ContentType'>} 
default router - : default contenttypes contenttype {} 
default router - : default contenttypes contenttype {'model': <class '__fake__.ContentType'>} 
 OK
  Applying auth.0002_alter_permission_name_max_length...default router - : default auth permission {'model': <class '__fake__.Permission'>} 
 OK
  Applying auth.0003_alter_user_email_max_length...default router - : default auth user {'model': <class '__fake__.User'>} 
 OK
  Applying auth.0004_alter_user_username_opts...default router - : default auth user {'model': <class '__fake__.User'>} 
 OK
  Applying auth.0005_alter_user_last_login_null...default router - : default auth user {'model': <class '__fake__.User'>} 
 OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages...default router - : default auth user {'model': <class '__fake__.User'>} 
 OK
  Applying auth.0008_alter_user_username_max_length...default router - : default auth user {'model': <class '__fake__.User'>} 
 OK
  Applying auth.0009_alter_user_last_name_max_length...default router - : default auth user {'model': <class '__fake__.User'>} 
 OK
  Applying auth.0010_alter_group_name_max_length...default router - : default auth group {'model': <class '__fake__.Group'>} 
 OK
  Applying auth.0011_update_proxy_permissions...default router - : default auth None {} 
 OK
  Applying auth.0012_alter_user_first_name_max_length...default router - : default auth user {'model': <class '__fake__.User'>} 
 OK
  Applying sessions.0001_initial...default router - : default sessions session {'model': <class '__fake__.Session'>} 
 OK
default router - : default contenttypes contenttype {'model': <class '__fake__.ContentType'>} 
default router - : default auth permission {'model': <class '__fake__.Permission'>} 
default router - : default contenttypes contenttype {'model': <class '__fake__.ContentType'>} 
default router - : default contenttypes contenttype {'model': <class '__fake__.ContentType'>} 
default router - : default auth permission {'model': <class '__fake__.Permission'>} 
default router - : default contenttypes contenttype {'model': <class '__fake__.ContentType'>} 
default router - : default contenttypes contenttype {'model': <class '__fake__.ContentType'>} 
default router - : default auth permission {'model': <class '__fake__.Permission'>} 
default router - : default contenttypes contenttype {'model': <class '__fake__.ContentType'>} 
default router - : default contenttypes contenttype {'model': <class '__fake__.ContentType'>} 
default router - : default auth permission {'model': <class '__fake__.Permission'>} 
default router - : default contenttypes contenttype {'model': <class '__fake__.ContentType'>} 

Use a good router, DefaultRouter.

I launch makemigrations with sharing in INSTALLED_APPS

./manage.py makemigrations --name sharing sharing
sharing router - : default sharing Gender {} 
default router - : default admin LogEntry {} 
sharing router - : musics_readers sharing Gender {} 
default router - : musics_readers admin LogEntry {} 
default router - : musics_readers sessions Session {} 
default router - : musics_readers auth Permission {} 
default router - : musics_readers auth Group {} 
default router - : musics_readers auth User {} 
default router - : musics_readers contenttypes ContentType {} 
sharing router - : musics_writers sharing Gender {} 
default router - : musics_writers admin LogEntry {} 
default router - : musics_writers sessions Session {} 
default router - : musics_writers auth Permission {} 
default router - : musics_writers auth Group {} 
default router - : musics_writers auth User {} 
default router - : musics_writers contenttypes ContentType {} 
sharing router - : sharing_readers sharing Gender {} 
default router - : sharing_readers admin LogEntry {} 
default router - : sharing_readers sessions Session {} 
default router - : sharing_readers auth Permission {} 
default router - : sharing_readers auth Group {} 
default router - : sharing_readers auth User {} 
default router - : sharing_readers contenttypes ContentType {} 
sharing router - : sharing_writers sharing Gender {} 
Migrations for 'sharing':
  sharing/migrations/0001_sharing.py
    - Create model Gender

I see when it's Gender model, use sharing router.

When I use migrate --plan, confirm it:

./manage.py migrate sharing --plan
default router - : default admin logentry {} 
default router - : default admin logentry {} 
default router - : default admin logentry {} 
default router - : default admin logentry {} 
default router - : default admin logentry {} 
default router - : default admin logentry {} 
default router - : default admin logentry {} 
default router - : default admin logentry {} 
default router - : default admin logentry {'model': <class 'django.contrib.admin.models.LogEntry'>} 
default router - : default admin logentry {'model': <class 'django.contrib.admin.models.LogEntry'>} 
default router - : default admin logentry {'model': <class 'django.contrib.admin.models.LogEntry'>} 
default router - : default auth permission {} 
default router - : default auth permission {} 
default router - : default auth permission {} 
default router - : default auth permission {} 
default router - : default auth permission {'model': <class 'django.contrib.auth.models.Permission'>} 
default router - : default auth permission {'model': <class 'django.contrib.auth.models.Permission'>} 
default router - : default auth permission {'model': <class 'django.contrib.auth.models.Permission'>} 
default router - : default auth group {} 
default router - : default auth group {} 
default router - : default auth group {} 
default router - : default auth group {'model': <class 'django.contrib.auth.models.Group'>} 
default router - : default auth group {'model': <class 'django.contrib.auth.models.Group'>} 
default router - : default auth group {'model': <class 'django.contrib.auth.models.Group'>} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {} 
default router - : default auth user {'model': <class 'django.contrib.auth.models.User'>} 
default router - : default auth user {'model': <class 'django.contrib.auth.models.User'>} 
default router - : default auth user {'model': <class 'django.contrib.auth.models.User'>} 
default router - : default contenttypes contenttype {} 
default router - : default contenttypes contenttype {} 
default router - : default contenttypes contenttype {} 
default router - : default contenttypes contenttype {'model': <class 'django.contrib.contenttypes.models.ContentType'>} 
default router - : default contenttypes contenttype {'model': <class 'django.contrib.contenttypes.models.ContentType'>} 
default router - : default contenttypes contenttype {'model': <class 'django.contrib.contenttypes.models.ContentType'>} 
default router - : default sessions session {} 
default router - : default sessions session {} 
default router - : default sessions session {} 
default router - : default sessions session {'model': <class 'django.contrib.sessions.models.Session'>} 
default router - : default sessions session {'model': <class 'django.contrib.sessions.models.Session'>} 
default router - : default sessions session {'model': <class 'django.contrib.sessions.models.Session'>} 

sharing router - : default sharing gender {} 
sharing router - : default sharing gender {} 
sharing router - : default sharing gender {'model': <class 'sharing.models.Gender'>} 
sharing router - : default sharing gender {'model': <class 'sharing.models.Gender'>} 
sharing router - : default sharing gender {'model': <class 'sharing.models.Gender'>} 
Planned operations:
sharing.0001_sharing
    Create model Gender

But when I lauch migrate command, sharing models tables not record.

...
Operations to perform:
  Apply all migrations: sharing
default router - : default contenttypes contenttype {'model': <class '__fake__.ContentType'>} 
Running migrations:
  Applying sharing.0001_sharing...sharing router - : default sharing gender {'model': <class '__fake__.Gender'>} 

 OK

I see the db is not good,

# print(f"sharing router - : {db} {app_label} {model_name} {hints} ")
sharing router - : default sharing gender {'model': <class '__fake__.Gender'>} 

But I return the good database 'sharing_writers' on SharingRouter.allow_migrate

Why not use the good database ?

I think I forgot something but I don't no what. I hope my explication is clear. Sorry for my english writing.

Thanks for your help.

Upvotes: 0

Views: 817

Answers (1)

Freddy
Freddy

Reputation: 1

I found the solution. In fact you need to use database parameter, like this:

./manage.py migrate sharing --database='sharing_writers'

I don't quite understand why I need to define it if I use routers. Why it is ont automatic ? And I find that the documentation it's not clear about this.

Well, I closed it.

Thanks

Upvotes: 0

Related Questions