I am trying to use a migrations.RunSQL
Django migration to run some arbitrary SQL code. I would like to run this migration for only certain db backends (for example only for postgres).
I would think to use something like this but I don't see the DB connection info in the Migration
class.
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.
As a part of the answer to the question "how does Django know what migrations have been run?", they store records of applied migrations in the database!
I just had the same need. I had to edit a migration that set the initial value of a sequence, which works on postgres but not sqlite. Here's how I wrapped the RunSQL inside a RunPython, following the documentation that Daniel linked to.
from django.db import migrations
def forwards(apps, schema_editor):
if not schema_editor.connection.vendor == 'postgres':
return
migrations.RunSQL(
"alter sequence api_consumer_id_seq restart with 1000500;")
class Migration(migrations.Migration):
dependencies = [
('api', '0043_auto_20160416_2313'),
]
operations = [
migrations.RunPython(forwards),
]
Here is how I solved the problem since I couldn't get RunSQL
to work inside RunPython
. Thankfully, the schema_editor
object has an execute()
method.
def forwards(apps, schema_editor):
if not schema_editor.connection.vendor.startswith('postgres'):
logger.info('Database vendor: {}'.format(schema_editor.connection.vendor))
logger.info('Skipping migration without attempting to ADD CONSTRAINT')
return
schema_editor.execute('ALTER TABLE my_table ADD CONSTRAINT my_constraint (my_field != \'NaN\';)')
def backwards(apps, schema_editor):
if not schema_editor.connection.vendor.startswith('postgres'):
logger.info('Database vendor: {}'.format(schema_editor.connection.vendor))
logger.info('Skipping migration without attempting to DROP CONSTRAINT')
return
schema_editor.execute('ALTER TABLE my_table DROP CONSTRAINT my_constraint;')
class Migration(migrations.Migration):
dependencies = [
...
]
operations = [
migrations.RunPython(forwards, backwards, atomic=True)
]
I solved a similar problem today -- needing to perform a migration to create a new model, but only for a postgres DB -- and I found this question. However, Matthew's answer did not help me. In fact, I'm not sure it works at all. That is because the line with migrations.RunSQL(...)
does not actually run SQL; it creates a new object of type RunSQL
which is a Command
, and then immediately discards it.
Here's how I ended up solving the problem, in case anyone tries to search for "django conditional migration" in the future:
from __future__ import unicode_literals
import django.contrib.postgres.fields
from django.db import migrations, models
class PostgresOnlyCreateModel(migrations.CreateModel):
def database_forwards(self, app_label, schema_editor, from_state, to_state):
if schema_editor.connection.vendor.startswith("postgres"):
super(PostgresOnlyCreateModel, self).database_forwards(app_label, schema_editor, from_state, to_state)
def database_backwards(self, app_label, schema_editor, from_state, to_state):
if schema_editor.connection.vendor.startswith("postgres"):
super(PostgresOnlyCreateModel, self).database_backwards(app_label, schema_editor, from_state, to_state)
class Migration(migrations.Migration):
dependencies = [
...whatever...
]
operations = [
PostgresOnlyCreateModel(
name='...whatever...',
fields=[...whatever...],
),
]
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