Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Migration RunSQL Conditional on Database Type

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.

like image 397
Alex Rothberg Avatar asked Sep 03 '15 20:09

Alex Rothberg


People also ask

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.

How does Django know which migrations to run?

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!


3 Answers

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),
    ]
like image 23
Matthew Avatar answered Sep 22 '22 15:09

Matthew


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)
    ]
like image 132
PaulMest Avatar answered Sep 23 '22 15:09

PaulMest


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...],
        ),
    ]
like image 25
Shaggy Frog Avatar answered Sep 25 '22 15:09

Shaggy Frog