django アプリケーションで使用しているテーブルの全削除、再作成を実施したところ、初期データとして必要なデータも消えてしまい、面倒なことになりました。
初期データをSQLで、migration の一貫として実行できると便利に思いましたので、SQLを直接実行する migration ファイルを作成してみました。
結果を以下に記載します。


前提

環境情報は以下の通りです。

  • OS

    sw_vers
    -----------------------
    ProductName:    Mac OS X
    ProductVersion: 10.12.5
    BuildVersion:   16F73
    -----------------------
    

  • python version

    python3 -V
    -----------------------
    Python 3.5.1
    -----------------------
    

  • Django Version

    pip3 list | grep Django
    -----------------------
    Django (1.10.7)
    -----------------------
    


migrations.RunSQL にINSERT文を直書きする

以下の手順で実施しました。

  • 空のマイグレーションファイルを作成する

  • マイグレーションファイルに、migrations.RunSQL で、INSERT 文を記載する。

  • migrate 実行

空のマイグレーションファイルを作成する

./manage.py makemigrations festivals4partypeople --empty
-----------------------------------------
Migrations for 'festivals4partypeople':
  festivals4partypeople/migrations/0002_auto_20170604_1454.py:
-----------------------------------------

マイグレーションファイルに、migrations.RunSQL で、INSERT 文を記載する。

以下のようにSQL文を直に記載します。
第2引数のDELETE 文は、リカバリ用のSQLになります。

# -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2017-06-04 05:54
from __future__ import unicode_literals

from django.db import migrations


class Migration(migrations.Migration):
    dependencies = [
        ('festivals4partypeople', '0001_squashed_0003_auto_20170411_0110'),
    ]

    operations = [
        # echonest_artist UNKNOWN DATA
        migrations.RunSQL(
            """INSERT INTO echonest_artist (id, echonest_id, name, relate_status, create_date, update_date) VALUES (-1, 
            'XXXXXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXXXXX', '2', 
            '1970-01-01 00:00:00.000+09', '1970-01-01 00:00:00.000+09');""",
            """DELETE FROM echonest_artist where id = -1;"""),
        # lastfm_artist UNKNOWN DATA
        migrations.RunSQL(
            """INSERT INTO lastfm_artist (id, mbid, name, relate_status, create_date, update_date) VALUES (-1,
            'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'XXXXXXXXXXXXXXXXXX', '2', '1970-01-01 00:00:00.000+09',
            '1970-01-01 00:00:00.000+09');""",
            """DELETE FROM lastfm_artist where id = -1;"""),
    ]

migrate 実行

  • コマンド実行
    ./manage.py migrate
    ---------------------------------------------
    Operations to perform:
      Apply all migrations: admin, auth, contenttypes, festivals4partypeople, sessions
    Running migrations:
      Applying festivals4partypeople.0002_auto_20170604_1454... OK
    ---------------------------------------------
    

migrations.RunSQL でSQLファイルを実行する

sqlparseインストールする前提ですが、RunSQL で、外だしのsql ファイルが実行できるようです。
ともと投入データをSQL文で記載していましたので、結局こちらの方法で、初期データ投入用のスクリプトは作成しました。
以下、参考になりました。

sqlparse のインストール

python3 -m pip install sqlparse

マイグレーションファイルを作成する

  • migrationファイル

load_sqlいうメソッドを作成しました。
複数のINSERT文を記載したSQLファイルを文字列で読み込み、
RunSQLの引数とします。

# -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2017-06-04 06:50
from __future__ import unicode_literals

from django.db import migrations


def load_sql(filename):
    f = open(filename)
    sql_statements = f.read()
    f.close()
    return sql_statements


class Migration(migrations.Migration):
    dependencies = [
        ('festivals4partypeople', '0002_auto_20170604_1454'),
    ]

    operations = [
        migrations.RunSQL(load_sql('festivals4partypeople/sqls/V1.0.0.1__Add_heldYears.sql')),
        migrations.RunSQL(load_sql('festivals4partypeople/sqls/V1.0.0.2__Add_festivals.sql')),
        migrations.RunSQL(load_sql('festivals4partypeople/sqls/V1.0.0.3__Add_artists.sql')),
    ]

  • sqlファイル

SQL ファイルの中身は以下のようなフォーマットです。
文末を;区切りにします。

INSERT INTO held_year (id, held_year, create_date, update_date) VALUES (nextval('held_year_id_seq'), '2010', current_date, current_date);
INSERT INTO held_year (id, held_year, create_date, update_date) VALUES (nextval('held_year_id_seq'), '2011', current_date, current_date);
INSERT INTO held_year (id, held_year, create_date, update_date) VALUES (nextval('held_year_id_seq'), '2012', current_date, current_date);
INSERT INTO held_year (id, held_year, create_date, update_date) VALUES (nextval('held_year_id_seq'), '2013', current_date, current_date);
INSERT INTO held_year (id, held_year, create_date, update_date) VALUES (nextval('held_year_id_seq'), '2014', current_date, current_date);
INSERT INTO held_year (id, held_year, create_date, update_date) VALUES (nextval('held_year_id_seq'), '2015', current_date, current_date);

migrate 実行

./manage.py migrate
---------------------------------------------
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, festivals4partypeople, sessions
Running migrations:
  Applying festivals4partypeople.0003_auto_20170604_1550... OK
---------------------------------------------


migrations.RunSQL にCREATE INDEX 文を直書きする

後日、CREATE INDEX文を実行する機会があったので追記します。
Django の Version は、2.1.8使用しました。

Executing Custom SQL in Django Migrations | End Point参考になりました。

INSERT文と同様に、INDEX作成などのDDL文も migrations.RunSQL実行できます。

  • migrationファイル

    # Generated by Django 2.1.8 on 2019-08-16 19:14
    
    from django.db import migrations
    
    
    class Migration(migrations.Migration):
    
        dependencies = [
            ('home', '0007_entrygooglesearchconsole'),
        ]
    
        operations = [
            migrations.RunSQL(
                """
                CREATE INDEX date_idx ON puput_entrypage (date);
                """,
                """
                DROP INDEX date_idx;
                """,
                )
        ]
    

  • Postgres 以外のデータベースの場合
    INSERT文では試していないのでわからないですが、CREATE INDEX を SQLite で実行した際は、SQL直書きの場合でも sqlparseインストールが必要でした。
    Executing Custom SQL in Django Migrations | End Point にも記載があるので引用します。

    Unless you’re using Postgres for your database, you’ll need to install the sqlparse library, which allows Django to break the SQL strings into individual statements.

以上です。

コメント