Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django migrations with multiple databases

Tags:

I have a hard time with creating data migrations. I use two databases for my apps. I configured databases in settings.py and also created a router like in Django docs.

# settings.py DB_HOST = 'localhost' DATABASES = { 'default': {     'ENGINE': 'django.db.backends.mysql',     'NAME': 'helios',     'HOST': DB_HOST,     'OPTIONS': {         'read_default_file': join(dirname(__file__), 'default.cnf'),     }, }, 'other': {     'ENGINE': 'django.db.backends.mysql',     'NAME': 'gala_pol',     'HOST': DB_HOST,     'OPTIONS': {         'read_default_file': join(dirname(__file__), 'other.cnf'),     }, },  DATABASE_APPS_MAPPING = {     'contenttypes': 'default',     'auth': 'default',     'admin': 'default',     'sessions': 'default',     'messages': 'default',     'staticfiles': 'default',     'woodsmen': 'default',     'helios': 'default',     'hush': 'default',     'hunt': 'other',     'meat': 'other',     'beast': 'other', }  # routers.py  class DatabaseAppsRouter(object):      def db_for_read(self, model, **hints):          if model._meta.app_label in settings.DATABASE_APPS_MAPPING:             return settings.DATABASE_APPS_MAPPING[model._meta.app_label]         return None      def db_for_write(self, model, **hints):          if model._meta.app_label in settings.             return settings.DATABASE_APPS_MAPPING[model._meta.app_label]         return None      def allow_relation(self, obj1, obj2, **hints):          db1 = settings.DATABASE_APPS_MAPPING.get(obj1._meta.app_label)         db2 = settings.DATABASE_APPS_MAPPING.get(obj2._meta.app_label)         if db1 and db2:             return db1 == db2         return None      def allow_migrate(self, db, app_label, model_name=None, **hints):          if db in settings.DATABASE_APPS_MAPPING.values():             return settings.DATABASE_APPS_MAPPING.get(app_label) == db     elif app_label in settings.DATABASE_APPS_MAPPING:             return False 

Here is the model and migrations of one of those apps:

# hunt.models.py  class Dish(models.Model):     """     Investigation case     """     display_name = models.CharField(max_length=64, unique=True)     department = models.ForeignKey(Kitchen, null=True)     case_type = models.PositiveSmallIntegerField(choices=CASE_TYPE_CHOICES, default=DEF_CASE_TYPE)     created_at = models.DateTimeField(blank=True, null=True)     comment = models.CharField(max_length=256, blank=True, null=True)      class Meta:         verbose_name = 'case'         app_label = 'hunt'      def __unicode__(self):         return (u'%s (%s)' % (self.display_name, self.created_at)).strip()   # hunt.migrations.0001_initial.py  class Migration(migrations.Migration):      app_label = 'hunt'      dependencies = [     ]      operations = [         migrations.CreateModel(             name='Dish',             fields=[                 ('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)),                 ('display_name', models.CharField(max_length=64, unique=True)),                 ('case_type', models.PositiveSmallIntegerField(default=0, choices=[(0, 'Unknown'), (1, 'General'), (2, 'Terror'), (3, 'Narco'), (4, 'Fraud'), (5, 'Slavery'), (6, 'Traffic'), (7, 'RICO'), (8, 'War'), (9, 'Cyber'), (20, 'Other')])),                 ('created_at', models.DateTimeField(null=True, blank=True)),                 ('comment', models.CharField(max_length=256, null=True, blank=True)),             ],             options={                 'verbose_name': 'case',             },         ),     ]  # hunt.migrations.0002_add_hunts.py   def create_initial_hunts(apps, schema_editor):      if settings.DEBUG:             print('\nContent added')   class Migration(migrations.Migration):     dependencies = [         ('hunt', '0001_initial'),     ]       operations = [         migrations.RunPython(create_initial_hunts, hints={'schema_editor': 'other'}),     ] 

The problem is: When i run "migrate" command, only applications that connected to default database are migrated. The migrations in rest of the apps are never run. If I launch migrate for such an app with --database option - it works fine.

How can I specify the database per migration? Isn't the router supposed to manage exactly this? Or I missed something else?

like image 492
Leon Kladnitsky Avatar asked Feb 24 '16 17:02

Leon Kladnitsky


People also ask

Can you use multiple databases within the same Django project?

Django's admin doesn't have any explicit support for multiple databases. If you want to provide an admin interface for a model on a database other than that specified by your router chain, you'll need to write custom ModelAdmin classes that will direct the admin to use a specific database for content.

What is the difference between Makemigrations and migrate in Django?

makemigrations is responsible for packaging up your model changes into individual migration files - analogous to commits - and migrate is responsible for applying those to your database.

Does Django migrate create database?

Since version 1.7, Django has come with built-in support for database migrations. In Django, database migrations usually go hand in hand with models: whenever you code up a new model, you also generate a migration to create the necessary table in the database.

What is a Django migrations?

Django migrations are nothing but Python Files. They contain Python code which is used by Django ORM to create and alter tables and fields in the database. Django implements Models and changes to them on the database via migrations. Django generates migrations automatically.

How to configure multiple databases in Django?

Since Django can work with multiple databases, you can have multiple database configurations. The default key is required to configure a single or multiple databases. Inside the default value, key resides the configuration for the database. The configuration can vary for different types of the database you are using.

Why is my Django database not matching my models?

You have not manually edited your database - Django won’t be able to detect that your database doesn’t match your models, you’ll just get errors when migrations try to modify those tables. Migrations can be reversed with migrate by passing the number of the previous migration.

How to create a Django project in multitenant?

Let’s create our Django project in the directory multitenant as shown: We need to register the app in the list of installed apps in settings.py under INSTALLED_APPS: In settings.py under DATABASES, delete the MySQLite configurations and replace them with: The snippet above tells Django that the default database to be called will be djongo.


1 Answers

You have to run migrate once for each database, specifying the target with --database. Each time it will consult your router to see which migrations to actually perform on that database.

I'm guessing it was designed this way to favor explicitness over implicitness. For example, your workflow might require you to migrate the different databases at different times.

Note, though, that you won't be able to tell from the output which migrations were actually performed, since:

If allow_migrate() returns False, any migration operations for the model_name will be silently skipped when running migrate on the db.

like image 160
Kevin Christopher Henry Avatar answered Oct 16 '22 01:10

Kevin Christopher Henry