Django dumpdata loaddata で、データを移行する


VPS 移行をして、Django dumpdata loaddata を使ってデータ移行を行いました。
過去のマイグレーション失敗や、そもそも除外しないと移行できない部分があって結構ハマりましたので、備忘としてやったことを記載します。

前提

移行前、移行後の環境について情報を記載します。

移行前

  • OS の Version

    CentOS 6.9
    

  • Django の Version

    Django                  1.10.8
    

  • Python の Version

    python 2.7.6
    

移行後

  • OS の Version

    CentOS Linux release 7.4.1708 (Core) 
    

  • Django の Version

    Django                  1.10.8
    

  • Python の Version

    Python 3.6.4
    


データ移行の手順 (初回)

以下の手順で実施しました。 1. 移行前環境で、daupdata を実行

python2.7 manage.py dumpdata > dump.json
2. 移行後の環境で、データ削除のため fulsh を実行
python3.6 manage.py flush
3. 移行後の環境で、 loaddata を実行
python3.6 manage.py loaddata dump.json


発生したトラブル

キー重複エラー1

全てのデータを投入したところ、以下のエラーが発生しました。

django.db.utils.IntegrityError: Problem installing fixture '/xxxx/dump.json': Could not load contenttypes.ContentType(pk=1): duplicate key value violates unique constraint "django_content_type_app_label_76bd3d3b_uniq"
DETAIL:  Key (app_label, model)=(auth, permission) already exists.
以下の記事の記載がありましたが、幾つか、除外しないといけないテーブルがあるようです。
DjangoをsqliteからPostgreSQLに切り替えた(dumpdata/loaddata) - [Dd]enzow(ill)? with DB and Python

  • 修正後のdumpdata
    python manage.py dumpdata --exclude auth.permission --exclude contenttypes > dump.json
    

RelatedObjectDoesNotExist

修正した dumpdata コマンドを実行したところ以下のエラーが発生しました。

django.db.models.fields.related_descriptors.RelatedObjectDoesNotExist: Problem installing fixture 'xxxx/dump.json': Comment has no content_type.    
Comment に、content_type がない状態でした。
showmigrations で、Migration の状況を見る限り、移行前、移行後の環境の適用状況は同じでしたが、DBのテーブル構造は確かに違うという状況でした。
原因はわからずですが、Comment のデータは削除済データで不要だったため、移行前の環境から、Comment関連のデータを削除しました。

  • コメント関連のデータを削除
    delete from django_comment_flags;
    delete from generic_threadedcomment;
    delete from django_comments;
    
    Comment 関連のテーブルは3テーブルあり、3テーブルのデータを物理削除しました。

violates foreign key constraint “django_admin_content_type_id_c4bce8eb_fk_django_content_type_id”

Comment データ削除後、以下のエラーが発生しました。

return self.cursor.execute(sql)
django.db.utils.IntegrityError: Problem installing fixtures: insert or update on table "django_admin_log" violates foreign key constraint "django_admin_content_type_id_c4bce8eb_fk_django_content_type_id"
DETAIL:  Key (content_type_id)=(43) is not present in table "django_content_type".
django_admin_log は、ADMIN での 操作履歴が格納されるテーブルです。どの Model に対して操作したのか django_content_type と関連づいたデータが設定されるようです。
前述の原因不明のMigration の 差分が影響しているようで、移行前環境のdjango_content_type と、移行後環境のdjango_content_type のデータが異なったため、admin のデータも除外しました。

python2.7 manage.py dumpdata --exclude admin --exclude auth.permission --exclude contenttypes > dump.json

format を 変更して dumpdata を試した

作業途中で、format を xml にして、dumpdata を実行したのですが、シリアライズできない文字列がありエラーが発生しました。

  • format xml の dumpdata コマンド

    python3 manage.py dumpdata --format=xml > django_dump.xml
    

  • エラー

    CommandError: Unable to serialize database: BlogPost.description (pk:6) contains unserializable characters
    

以下のようなスクリプトを書いて、対象のデータを特定しましたが、対象 Blog 記事には確かにシリアライズできなそうな文字が含まれており、削除したところ、dumpできました。

  • スクリプト
      import codecs
      from mezzanine.blog.models import BlogPost
      from django.core import serializers
      for blog_post in BlogPost.objects.all():
        print("###################")
        print(blog_post)
        data = serializers.serialize("xml", BlogPost.objects.all())
        print(data) 
    

データ移行の手順 (トラブル解消後)

トラブル解消後の手順は以下になります。
1. 移行前環境で、daupdata を実行

python2.7 manage.py dumpdata --exclude admin --exclude auth.permission --exclude contenttypes > dump.json
2. 移行後の環境で、データ削除のため fulsh を実行
python3.6 manage.py flush
3. 移行後の環境で、 loaddata を実行
python3.6 manage.py loaddata dump.json

以上です。

コメント