/ PythonDjango

Djangoで、初期データの投入

Djangoで、初期データを投入したい場合の方法を2つ紹介していきます。

fixtureを使う

fixtureというDjangoの機能でやってみましょう。

これは結構便利で、テストデータの管理や開発時の初期データとして汎用的に使えますし、JSONといったファイルで管理できるのも魅力的です。

次のような、シンプルなモデルを例に説明していきます。

from django.db import models


class Post(models.Model):
    title = models.CharField('タイトル', max_length=255)

    def __str__(self):
        return self.title

アプリケーションのディレクトリ直下に、fixturesというディレクトリを作ります。そして、post_initial.jsonというファイルを作り、次のようにしておきます(ファイル名は任意につけれます)。

[
  {
    "model": "app.post",
    "pk": 1,
    "fields": {
      "title": "おはよう"
    }
  },
  {
    "model": "app.post",
    "pk": 2,
    "fields": {
      "title": "こんにちは"
    }
  }
]

今回、アプリケーション名はappです。modelにはアプリケーション名.モデル名とし、pkはそのままpkを、fieldsはモデルのフィールドに対応します。この例ならば、タイトルが「おはよう」と「こんにちは」のPostデータを表しています。

次のコマンドで、実際にデータとして作成されます。

python manage.py loaddata post_initial.json

JSONのほか、XMLやYAML形式でも書くことができます。

dumpdataコマンド

モデルのフィールドが多いと、JSON等のファイルをコツコツ作るのは手間です。dumpdataコマンドを使うことで、loaddataできるデータ...つまり、上のようなJSON等のファイルを自動で作成することができます。

実際のところ、admin管理画面等で、ある程度データを投入してからそれをdumpするほうが多いです。

次のコマンドは、appアプリケーション内の全てのデータをdumpします。

python manage.py dumpdata app > app.json

> app.jsonとして、出力をリダイレクト...つまりファイルに書き込んでいます。

ある特定のモデルならば、次のようにします。

python manage.py dumpdata app.post > app_post.json

全てのデータをdumpするならば、アプリケーションやモデル部分を省略すればできます。もちろん、ユーザー等のデータもdumpされます。

python manage.py dumpdata > all.json

データのちょっとしたバックアップにも使えますね。

テストでfixtureを読み込む

Djangoでテストを行う際、このようなfixtureからテスト用データを読み込むこともできます。

最初に作ったpost_initial.jsonをテスト用データとして使うならば、次のようになります。

from django.shortcuts import reverse
from django.test import TestCase


class PostIndexViewTests(TestCase):
    fixtures = ['post_initial']

    def test_get(self):
        response = self.client.get(reverse('app:post_list'))
        self.assertContains(response, 'おはよう')
        self.assertContains(response, 'こんにちは')
        self.assertQuerysetEqual(response.context['post_list'],  ['<Post: おはよう>', '<Post: こんにちは>'])

fixtures = ['post_initial']の部分で、読み込むfixtureを指定します。

post_migrateシグナルを使う

これはpython manage.py migrateコマンドの際に、初期データも一緒に登録するという方法です。Django組み込みのモデルでも、幾つかこれでデータが作成されています。

fixtureと違うのは、migrate後に必ずデータが追加されるという点です。ある意味、そのデータの追加が強制されているとも言えます。

そもそも幾つかのデータがないとロジック上問題となる...例えば決められたGroupが幾つか必要だとか、そういった場合には有効です。loaddataコマンドを忘れずに行うという運用でも解決するかもしれませんが、それが信用できなかったり、面倒な場合でも安心できる方法です。

まず、apps.pyを次のようにします。

from django.apps import AppConfig
from django.db.models.signals import post_migrate


class AppConfig(AppConfig):
    name = 'app'

    def ready(self):
        from .models import create_default_group
        post_migrate.connect(create_default_group, sender=self)

そして、models.pyに次のように書きます。

def create_default_group(sender, **kwargs):
    Group.objects.get_or_create(name='投稿者')
    Group.objects.get_or_create(name='編集者')

migrateのたびに呼ばれるので、既にデータがある場合は作成しないようにしましょう。なので、get_or_createとしています。

models.pyに書いていますが、他の場所でも良いです。Django標準の各モデルは、managementから初期データ作成用の機能を呼び出しています。

Userの作成

もしこの方法でUserを作りたいならば、例えば次のようになります。Userをプログラム上から作成する場合は、パスワードの設定作業が必要なのでちょっと複雑です(UserManagercreate_user()create_superuser()だと少し、少しだけ問題があるので面倒な書き方をしています)。

    user, created = User.objects.get_or_create(
        username='narito', email='toritoritorina@gmail.com',
        is_superuser=False, is_staff=True, is_active=True
    )
    # ユーザーが今作られたならば、新規作成なので、パスワードを設定
    if created:
        user.set_password('helloworld')
        user.save()

    user, created = User.objects.get_or_create(
        username='admin', email='admin@admin.com',
        is_superuser=True, is_staff=True, is_active=True
    )
    if created:
        user.set_password('helloworld')
        user.save()