Make migration conditional in Django
Specific use case:
The Django module wants to create an extension during migration. If the database user that is used to run the migration is not a superuser, this crashes.
There are several ways to solve this problem, one way (hypothetically) is to check the migration file if the extension is installed and only run that SQL code if it is not installed.
However, after some research, it looks like Django RunSQL
can return results and subsequently exclude operations based on the result of the previous operation, is not possible. Will there be other ways to achieve this? (For example subclasses RunSQL
?)
Any solution based on Django migrations, Django settings, or Postgres internal (one SQL statement that only lets you run CREATE EXTENSION
if some condition is true) will be fine.
(Note that I mentioned django-pgcrypto-fields for illustration purposes only. I'm curious to know if such a solution even exists.)
EDIT in response to Anentropic's comment: The solution should work when running commands test
or jenkins
. This means that calling manually --fake-initial
or similar to avoid running this migration is not an option. If you can explain how to test
spoof certain migrations, that is very welcome.
My current solution is as follows settings
:
MIGRATION_MODULES = {
'pgcrypto_fields': 'do-not-run-migrations'
}
But this will disable all listed migrations, not just offensive ones. In this case it might work, but I see it as a lucky and ugly job.
source to share
In fact, Django HStoreExtension only works when needed. But of course it still requires superuser access. A class similar to HStoreExtension can be created for pgcrypto.
The setup to run in tests still requires superuser access, so the setup should look like this:
INSTALLED_APPS = (
...
'django.contrib.postgres',
...
)
if sys.argv[1] in ['test', 'jenkins']:
# Install extension hstore (requires superuser)
INSTALLED_APPS = ('test_init',) + INSTALLED_APPS
# a superuser
DATABASES["default"]["USER"] = 'xxx'
DATABASES["default"]["PASSWORD"] = 'xxx'
and django app 'test_init' contains only the required __init.py__
and migration module with the following file 0001_initial.py
:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.contrib.postgres.operations import HStoreExtension
from django.db import migrations
class Migration(migrations.Migration):
run_before = [
('some_app_that_requires_hstore', '0001_initial'),
]
operations = [
HStoreExtension(),
]
It doesn't really need to check the test mode (through sys.argv
) to do the migration only then. As mentioned above, HStoreExtension is not error aware. So even if you run migrate
in production without superuser it won't fail, no matter if hstore is installed. See its source code.
See also Django documentation for HStoreField .
source to share