1

I have a django projects that has 2 apps and each app runs on a different DB (lets call them default and DB2). I have created a router to manage the data and added the router to my settings.py

When I run migrate I get Applying analytics.0001_initial... OK but in the default DB the only thing that gets updated is the django_migrations table showing that the app analytics has been migrated with 0001, and in the DB2 the table itself isn’t even created at all.

Going to the django shell and trying to do obj.objects.all() gives me table DB2.table_name does not exist I also verified in sql DB2 exists but doesn’t have any table from what I created

My router:

class DB2Router(object):
    """
    A router for apps that connect directly to the DB2 database.

    This router allows apps to directly update the DB2  database rather
    than using raw SQL as in the past. The router forces the use of the DB2
    database for any app in the "apps" list.
    """
    db = "DB2"
    apps = ["analytics"]

    def db_for_read(self, model, **hints):
        if model._meta.app_label in self.apps:
            return self.db
        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label in self.apps:
            return self.db
        return None

    def allow_relation(self, obj1, obj2, **hints):
        # Allow any relation between two models that are both in the same app.
        if (obj1._meta.app_label in self.apps) and (obj2._meta.app_label  in self.apps):
            return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if app_label in self.apps:
            return db == self.db
        return None

My model:

class PartnerImpression(models.Model):
    """ used to track impressions on widgets through our partners """
    partner = models.CharField(max_length=1024)
    referer = models.CharField(default="N/A", max_length=2048)
    created = models.DateTimeField(auto_now_add=True, blank=True)

The migration:

class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='PartnerImpression',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('partner', models.CharField(max_length=1024)),
                ('referer', models.CharField(default='N/A', max_length=2048)),
                ('created', models.DateTimeField(auto_now_add=True)),
            ],
        ),
    ]

  • DB2 exists and has an empty django_migrations table.

  • Defining the database in the manage.py migrate --database=DB2 command is not an option since I run a CI process on dozens of servers and can't run this manually on all of them so the command needs to stay without arguments.

  • I don't want any raw SQL in my migrations

I have also found this: Django migrations with multiple databases but I don't know if anything has changed since 2016, and also hoping for a way to get it running without the database option

elad silver
  • 9,222
  • 4
  • 43
  • 67

1 Answers1

1

By default migrate takes --database value as default (DEFAULT_DB_ALIAS), if not provided; hence applies all the migrations for that database. If you want to use what Django provides out-of-the-box, you have to use --database mentioning which database to operate on. If for some reason, you can't use that (like you mentioned), you can create a custom management command e.g. migrate_all to apply all your migrations to all databases at once.

An example approach below with only showing how the handle method can look like, you can take inspiration from migrate:

migrate_all.py:

from importlib import import_module
from django.apps import apps
from django.db import connections

class Command(BaseCommand):
    help = "Updates all database schemas."

    def add_arguments(self, parser):
        # Add arguments if you want
        pass

    def handle(self, *args, **options):
        # Import the 'management' module within each installed app, to register
        # dispatcher events.
        for app_config in apps.get_app_configs():
            if module_has_submodule(app_config.module, "management"):
                import_module('.management', app_config.name)

        # Iterate over and operate on each of the databases
        for connection in connections.databases:
            # https://github.com/django/django/blob/master/django/core/management/commands/migrate.py#L84
            connection.prepare_database()
            ...
            ...

After getting each connection from connections.databases, follow the operations from migrations for each.

heemayl
  • 39,294
  • 7
  • 70
  • 76