Raul Albarran
Raul Albarran

Reputation: 11

How to use multiple databases in Django with an empty 'default'?

I'm trying to make a multiple database project in Django. But I'm having problems to properly migrate my models to the appropriate database.

I'm using the 'Writing your first Django app' tutorial as a starting point; so ... I've written the following database routers; this one for 'polls' app:

class PollsRouter:

route_app_labels = {'polls'}

def db_for_read(self, model, **hint):
    if model._meta.app_label == 'polls':
        return 'Polls'
    return None

def db_for_rite(self, model, **hint):
    if model._meta.app_label == 'polls':
        return 'Polls'
    return None

def allow_relation(self, obj1, obj2, **hint):
    if (
        obj1._meta.app_label == 'polls' or
        obj2._meta.app_label == 'polls'
    ):
        return True
    return None

def allow_migrates(self, db, app_label, model_name=None, **hints):
    if app_label == 'polls':
        return db == 'Polls'
    return None

Also the router class for 'devices' app:

class DevicesRouter:

route_app_labels = {'devices'}

def db_for_read(self, model, **hint):
    if model._meta.app_label == 'devices':
        return 'CTS_ER_BD'
    return None

def db_for_rite(self, model, **hint):
    if model._meta.app_label == 'devices':
        return 'CTS_ER_BD'
    return None

def allow_relation(self, obj1, obj2, **hint):
    if (
        obj1._meta.app_label == 'devices' or
        obj2._meta.app_label == 'devices'
    ):
        return True
    return None

def allow_migrates(self, db, app_label, model_name=None, **hints):
    if app_label == 'devices':
        return db == 'CTS_ER_BD'
    return None

And finally, the router class for 'auth_user' database (for native Django apps)

class Auth_userRouter:

    route_app_labels = {'auth', 'contenttypes', 'admin', 'sessions'}

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

    def db_for_write(self, model, **hints):
        if model._meta.app_label in self.route_app_labels:
            return 'Auth_user_db'
        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 == 'Auth_user_db'
        return None

Of course ... I've included my apps and routers in the settings.py as this:

DATABASES = {
    'default': {},
    'Auth_user_db': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'auth_db',
        'HOST': '172.18.0.2',
        'USER': 'root',
        'PASSWORD': 'pass'
    },
    'CTS_ER_BD': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'CTS_ER_BD',
        'HOST': '172.18.0.2',
        'USER': 'root',
        'PASSWORD': 'pass'
    },
    'Polls': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'polls',
        'HOST': '172.18.0.2',
        'USER': 'root',
        'PASSWORD': 'pass'
    }

}

DATABASE_ROUTERS = [
        'routers.Polls_router.PollsRouter',
        'routers.Devices_router.DevicesRouter',
        'routers.Auth_user_router.Auth_userRouter'
    ]

The problem comes when I try to perform the migrations, this is the tree of my project before creating migrations files:

(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ tree .
.
├── devices
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── manage.py
├── multiple-DB
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── polls
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
└── routers
    ├── Auth_user_router.py
    ├── Devices_router.py
    └── Polls_router.py

4 directories, 23 files

Apparently migrations are properly created as:

(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ python3 manage.py makemigrations polls
Migrations for 'polls':
  polls/migrations/0001_initial.py
    - Create model Question
    - Create model Choice
(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ python3 manage.py makemigrations devices
Migrations for 'devices':
  devices/migrations/0001_initial.py
    - Create model AuthUsers
    - Create model Devices
    - Create model DeviceTypes
    - Create model FirmwareVers
    - Create model HardwareVers
    - Create model Historicals
    - Create model Metadata
    - Create model Projects
    - Create model SoftwareVers
    - Create model Tests
(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ tree .
.
├── devices
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   ├── __init__.py
│   │   └── __pycache__
│   │       └── __init__.cpython-39.pyc
│   ├── models.py
│   ├── __pycache__
│   │   ├── admin.cpython-39.pyc
│   │   ├── apps.cpython-39.pyc
│   │   ├── __init__.cpython-39.pyc
│   │   ├── models.cpython-39.pyc
│   │   ├── urls.cpython-39.pyc
│   │   └── views.cpython-39.pyc
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── manage.py
├── multiple-DB
│   ├── asgi.py
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-39.pyc
│   │   ├── settings.cpython-39.pyc
│   │   └── urls.cpython-39.pyc
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── polls
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   ├── __init__.py
│   │   └── __pycache__
│   │       ├── 0001_initial.cpython-39.pyc
│   │       └── __init__.cpython-39.pyc
│   ├── models.py
│   ├── __pycache__
│   │   ├── admin.cpython-39.pyc
│   │   ├── apps.cpython-39.pyc
│   │   ├── __init__.cpython-39.pyc
│   │   ├── models.cpython-39.pyc
│   │   ├── urls.cpython-39.pyc
│   │   └── views.cpython-39.pyc
│   ├── tests.py
│   ├── urls.py
│   └── views.py
└── routers
    ├── Auth_user_router.py
    ├── Devices_router.py
    ├── Polls_router.py
    └── __pycache__
        ├── Auth_user_router.cpython-39.pyc
        ├── Devices_router.cpython-39.pyc
        └── Polls_router.cpython-39.pyc

12 directories, 48 files

The problem is that when I check the sqlmigrate output for devices app I can't see a proper SQL script:

(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ python3 manage.py sqlmigrate devices 0001 --database=CTS_ER_BD
--
-- Create model AuthUsers
--
--
-- Create model Devices
--
--
-- Create model DeviceTypes
--
--
-- Create model FirmwareVers
--
--
-- Create model HardwareVers
--
--
-- Create model Historicals
--
--
-- Create model Metadata
--
--
-- Create model Projects
--
--
-- Create model SoftwareVers
--
--
-- Create model Tests
--
(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ 

This is causing that when I run python3 manage.py migrate devices --database=CTS_ER_BD for devices:

(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ python3 manage.py migrate devices --database=CTS_ER_BD
Operations to perform:
  Apply all migrations: devices
Running migrations:
  Applying devices.0001_initial... OK

Tables in thi guys, I'm trying to make a multiple database project in Django. But I'm having problems to properly migrate my models to the appropriate database.

I'm using the 'Writing your first Django app' tutorial as a starting point; so ... I've written the following database routers; this one for 'polls' app:

class PollsRouter:

route_app_labels = {'polls'}

def db_for_read(self, model, **hint):
    if model._meta.app_label == 'polls':
        return 'Polls'
    return None

def db_for_rite(self, model, **hint):
    if model._meta.app_label == 'polls':
        return 'Polls'
    return None

def allow_relation(self, obj1, obj2, **hint):
    if (
        obj1._meta.app_label == 'polls' or
        obj2._meta.app_label == 'polls'
    ):
        return True
    return None

def allow_migrates(self, db, app_label, model_name=None, **hints):
    if app_label == 'polls':
        return db == 'Polls'
    return None

Also the router class for 'devices' app:

class DevicesRouter:

route_app_labels = {'devices'}

def db_for_read(self, model, **hint):
    if model._meta.app_label == 'devices':
        return 'CTS_ER_BD'
    return None

def db_for_rite(self, model, **hint):
    if model._meta.app_label == 'devices':
        return 'CTS_ER_BD'
    return None

def allow_relation(self, obj1, obj2, **hint):
    if (
        obj1._meta.app_label == 'devices' or
        obj2._meta.app_label == 'devices'
    ):
        return True
    return None

def allow_migrates(self, db, app_label, model_name=None, **hints):
    if app_label == 'devices':
        return db == 'CTS_ER_BD'
    return None

And finally, the router class for 'auth_user' database (for native Django apps)

class Auth_userRouter:

    route_app_labels = {'auth', 'contenttypes', 'admin', 'sessions'}

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

    def db_for_write(self, model, **hints):
        if model._meta.app_label in self.route_app_labels:
            return 'Auth_user_db'
        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 == 'Auth_user_db'
        return None

Of course ... I've included my apps and routers in the settings.py as this:

DATABASES = {
    'default': {},
    'Auth_user_db': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'auth_db',
        'HOST': '172.18.0.2',
        'USER': 'root',
        'PASSWORD': 'pass'
    },
    'CTS_ER_BD': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'CTS_ER_BD',
        'HOST': '172.18.0.2',
        'USER': 'root',
        'PASSWORD': 'pass'
    },
    'Polls': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'polls',
        'HOST': '172.18.0.2',
        'USER': 'root',
        'PASSWORD': 'pass'
    }

}

DATABASE_ROUTERS = [
        'routers.Polls_router.PollsRouter',
        'routers.Devices_router.DevicesRouter',
        'routers.Auth_user_router.Auth_userRouter'
    ]

The problem comes when I try to perform the migrations, this is the tree of my project before creating migrations files:

(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ tree .
.
├── devices
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── manage.py
├── multiple-DB
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── polls
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
└── routers
    ├── Auth_user_router.py
    ├── Devices_router.py
    └── Polls_router.py

4 directories, 23 files

Apparently migrations are properly created as:

(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ python3 manage.py makemigrations polls
Migrations for 'polls':
  polls/migrations/0001_initial.py
    - Create model Question
    - Create model Choice
(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ python3 manage.py makemigrations devices
Migrations for 'devices':
  devices/migrations/0001_initial.py
    - Create model AuthUsers
    - Create model Devices
    - Create model DeviceTypes
    - Create model FirmwareVers
    - Create model HardwareVers
    - Create model Historicals
    - Create model Metadata
    - Create model Projects
    - Create model SoftwareVers
    - Create model Tests
(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ tree .
.
├── devices
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   ├── __init__.py
│   │   └── __pycache__
│   │       └── __init__.cpython-39.pyc
│   ├── models.py
│   ├── __pycache__
│   │   ├── admin.cpython-39.pyc
│   │   ├── apps.cpython-39.pyc
│   │   ├── __init__.cpython-39.pyc
│   │   ├── models.cpython-39.pyc
│   │   ├── urls.cpython-39.pyc
│   │   └── views.cpython-39.pyc
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── manage.py
├── multiple-DB
│   ├── asgi.py
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-39.pyc
│   │   ├── settings.cpython-39.pyc
│   │   └── urls.cpython-39.pyc
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── polls
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   ├── __init__.py
│   │   └── __pycache__
│   │       ├── 0001_initial.cpython-39.pyc
│   │       └── __init__.cpython-39.pyc
│   ├── models.py
│   ├── __pycache__
│   │   ├── admin.cpython-39.pyc
│   │   ├── apps.cpython-39.pyc
│   │   ├── __init__.cpython-39.pyc
│   │   ├── models.cpython-39.pyc
│   │   ├── urls.cpython-39.pyc
│   │   └── views.cpython-39.pyc
│   ├── tests.py
│   ├── urls.py
│   └── views.py
└── routers
    ├── Auth_user_router.py
    ├── Devices_router.py
    ├── Polls_router.py
    └── __pycache__
        ├── Auth_user_router.cpython-39.pyc
        ├── Devices_router.cpython-39.pyc
        └── Polls_router.cpython-39.pyc

12 directories, 48 files

The problem is that when I check the sqlmigrate output for devices app I can't see a proper SQL script:

(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ python3 manage.py sqlmigrate devices 0001 --database=CTS_ER_BD
--
-- Create model AuthUsers
--
--
-- Create model Devices
--
--
-- Create model DeviceTypes
--
--
-- Create model FirmwareVers
--
--
-- Create model HardwareVers
--
--
-- Create model Historicals
--
--
-- Create model Metadata
--
--
-- Create model Projects
--
--
-- Create model SoftwareVers
--
--
-- Create model Tests
--
(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ 

This is causing that when I run python3 manage.py migrate devices --database=CTS_ER_BD for devices:

(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ python3 manage.py migrate devices --database=CTS_ER_BD
Operations to perform:
  Apply all migrations: devices
Running migrations:
  Applying devices.0001_initial... OK

Tables in the corresponding database aren't being created:

MySQL [CTS_ER_BD]> SHOW TABLES;
+---------------------+
| Tables_in_CTS_ER_BD |
+---------------------+
| django_migrations   |
+---------------------+

But apparently the migration for devices app is properly generated! (I'm not pasting it to don't make this post longer)

So I don't understand why sqlmigrate is not showing a proper sql script. Or the migration isn't properly performed.

I'm not sure if this may be caused by the fact both migration files are actually called in the same way (0001_initial.py), should I choose a different one for each app? Anyway ... both are in different folders, so I don't see any problem with this.

Migrations for polls and Django native apps are properly working (apparently), in fact for 'polls' app you can see a proper output for sqlmigrate

(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ python3 manage.py sqlmigrate polls 0001 --database=Polls
--
-- Create model Question
--
CREATE TABLE `polls_question` (`id` bigint AUTO_INCREMENT NOT NULL PRIMARY KEY, `question_text` varchar(200) NOT NULL, `pub_date` datetime(6) NOT NULL);
--
-- Create model Choice
--
CREATE TABLE `polls_choice` (`id` bigint AUTO_INCREMENT NOT NULL PRIMARY KEY, `choice_text` varchar(200) NOT NULL, `votes` integer NOT NULL, `question_id` bigint NOT NULL);
ALTER TABLE `polls_choice` ADD CONSTRAINT `polls_choice_question_id_c5b4b260_fk_polls_question_id` FOREIGN KEY (`question_id`) REFERENCES `polls_question` (`id`);
(venv) raul@raul:~/multiple-db-in-django/multiple-DB$

So I'm not sure what I'm doing wrong. If you want to see the project ... I'm tracking it in GitLab https://gitlab.com/ralbarran1/multiple-db-in-django

Thank you very much, I'm sorry if the question is too long or if it's duplicated, I can see several similar questions, but too old I think.

he corresponding database aren't being created:

MySQL [CTS_ER_BD]> SHOW TABLES;
+---------------------+
| Tables_in_CTS_ER_BD |
+---------------------+
| django_migrations   |
+---------------------+

But apparently the migration for devices app is properly generated!:

(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ cat devices/migrations/0001_initial.py 
# Generated by Django 4.0.4 on 2022-04-29 09:39

from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='AuthUsers',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('user_name', models.CharField(max_length=50)),
            ],
            options={
                'db_table': 'auth_users',
                'managed': False,
            },
        ),
        migrations.CreateModel(
            name='Devices',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('serial_number', models.IntegerField()),
                ('manufacture_date', models.DateTimeField(blank=True, null=True)),
                ('devices_tag', models.ImageField(blank=True, null=True, upload_to='')),
            ],
            options={
                'db_table': 'devices',
                'managed': False,
            },
        ),
        migrations.CreateModel(
            name='DeviceTypes',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('device_name', models.CharField(max_length=50)),
                ('client_ref', models.CharField(max_length=100)),
                ('colway_ref', models.CharField(max_length=100)),
                ('power_dis', models.IntegerField(blank=True, null=True)),
            ],
            options={
                'db_table': 'device_types',
                'managed': False,
            },
        ),
        migrations.CreateModel(
            name='FirmwareVers',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('firmware_name', models.CharField(blank=True, max_length=50, null=True)),
                ('manuf_type', models.CharField(blank=True, max_length=50, null=True)),
            ],
            options={
                'db_table': 'firmware_vers',
                'managed': False,
            },
        ),
        migrations.CreateModel(
            name='HardwareVers',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('hw_type', models.CharField(blank=True, db_column='HW_type', max_length=1, null=True)),
            ],
            options={
                'db_table': 'hardware_vers',
                'managed': False,
            },
        ),
        migrations.CreateModel(
            name='Historicals',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('test_date', models.DateTimeField(blank=True, null=True)),
                ('reuslt', models.BooleanField(blank=True, null=True)),
            ],
            options={
                'db_table': 'historicals',
                'managed': False,
            },
        ),
        migrations.CreateModel(
            name='Metadata',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
            ],
            options={
                'db_table': 'metadata',
                'managed': False,
            },
        ),
        migrations.CreateModel(
            name='Projects',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('project_name', models.CharField(blank=True, max_length=50, null=True)),
            ],
            options={
                'db_table': 'projects',
                'managed': False,
            },
        ),
        migrations.CreateModel(
            name='SoftwareVers',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('colway_ref', models.CharField(blank=True, max_length=100, null=True)),
                ('joifi_ref', models.CharField(blank=True, max_length=100, null=True)),
            ],
            options={
                'db_table': 'software_vers',
                'managed': False,
            },
        ),
        migrations.CreateModel(
            name='Tests',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('test_name', models.CharField(blank=True, max_length=50, null=True)),
                ('prefix', models.CharField(blank=True, max_length=1, null=True)),
            ],
            options={
                'db_table': 'tests',
                'managed': False,
            },
        ),
    ]

So I don't understand why sqlmigrate is not showing a proper sql script. Or the migration isn't properly performed.

I'm not sure if this may be caused by the fact both migration files are actually called in the same way (0001_initial.py), should I choose a different one for each app? Anyway ... both are in different folders, so I don't see any problem with this.

Migrations for polls and Django native apps are properly working (apparently), in fact for 'polls' app you can see a proper output for sqlmigrate

(venv) raul@raul:~/multiple-db-in-django/multiple-DB$ python3 manage.py sqlmigrate polls 0001 --database=Polls
--
-- Create model Question
--
CREATE TABLE `polls_question` (`id` bigint AUTO_INCREMENT NOT NULL PRIMARY KEY, `question_text` varchar(200) NOT NULL, `pub_date` datetime(6) NOT NULL);
--
-- Create model Choice
--
CREATE TABLE `polls_choice` (`id` bigint AUTO_INCREMENT NOT NULL PRIMARY KEY, `choice_text` varchar(200) NOT NULL, `votes` integer NOT NULL, `question_id` bigint NOT NULL);
ALTER TABLE `polls_choice` ADD CONSTRAINT `polls_choice_question_id_c5b4b260_fk_polls_question_id` FOREIGN KEY (`question_id`) REFERENCES `polls_question` (`id`);
(venv) raul@raul:~/multiple-db-in-django/multiple-DB$

So I'm not sure what I'm doing wrong. If you want to see the project ... I'm tracking it in GitLab https://gitlab.com/ralbarran1/multiple-db-in-django

Thank you very much, I'm sorry if the question is too long or if it's duplicated, I can see several similar questions, but too old I think.

Upvotes: 0

Views: 857

Answers (1)

Raul Albarran
Raul Albarran

Reputation: 11

The problem actually was in the model:

class DeviceTypes(models.Model):
    device_name = models.CharField(max_length=50)
    client_ref = models.CharField(max_length=100)
    colway_ref = models.CharField(max_length=100)
    power_dis = models.IntegerField(blank=True, null=True)

    def __str__(self):
        return f'{self.colway_ref}', {self.device_name}

    class Meta:
        managed = Fase
        db_table = 'device_types'

This META OPTIONS managed = False was preventing Django from managing this model.

This was because I let Django to generate the model from an SQL script with python manage.py inspectdb > models.py and this sets by default managed = False

Upvotes: 1

Related Questions