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?
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.
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.
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.
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.
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.
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.
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.
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()
returnsFalse
, any migration operations for themodel_name
will be silently skipped when runningmigrate
on thedb
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With