似非プログラマのうんちく

「似非プログラマの覚え書き」出張版

巳年じゃないけど Python やろうぜ(その 15)

初期データの一括投入

今回から、当ブログではすっかりお馴染みの「政令指定都市一覧」を使って、Django の様々な機能を見ていきます。

$ ./manage.py startapp major_city

例によってアプリを作成するところからスタート。

Model を作ります。

from django.db import models

# Create your models here.


class District(models.Model):
    name = models.CharField(max_length=30)


class Prefecture(models.Model):
    name = models.CharField(max_length=30)
    district = models.ForeignKey(District, on_delete=models.CASCADE)


class City(models.Model):
    name = models.CharField(max_length=30)
    prefecture = models.ForeignKey(Prefecture, on_delete=models.CASCADE)
    designated = models.DateField()
    area = models.DecimalField(max_digits=7, decimal_places=2)
    population = models.IntegerField()

ForeignKey が初登場しました。これは参照する親モデルのクラス名と、親クラスを削除した時の挙動(ON DELETE ~)を指定します。ちなみに on_deleteDjango 2.0 以降で必須になるそうです*1ので、デフォルトに甘えずに指定しておくと移行が楽になるでしょう。

マイグレーションを作成します。

$ ./manage.py makemigrations major_city
(0.001) 
            SELECT name, type FROM sqlite_master
            WHERE type in ('table', 'view') AND NOT name='sqlite_sequence'
            ORDER BY name; args=None
(0.000) SELECT "django_migrations"."app", "django_migrations"."name" FROM "django_migrations"; args=()
Migrations for 'major_city':
  major_city/migrations/0001_initial.py:
    - Create model City
    - Create model District
    - Create model Prefecture
    - Add field prefecture to city

SQL を確認してみましょう。

$ ./manage.py sqlmigrate major_city 0001
(中略)
BEGIN;
--
-- Create model City
--
CREATE TABLE "major_city_city" (
    "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
    "name" varchar(30) NOT NULL,
    "designated" date NOT NULL,
    "area" decimal NOT NULL,
    "population" integer NOT NULL
);
--
-- Create model District
--
CREATE TABLE "major_city_district" (
    "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
    "name" varchar(30) NOT NULL
);
--
-- Create model Prefecture
--
CREATE TABLE "major_city_prefecture" (
    "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
    "name" varchar(30) NOT NULL,
    "district_id" integer NOT NULL REFERENCES "major_city_district" ("id")
);
--
-- Add field prefecture to city
--
ALTER TABLE "major_city_city" RENAME TO "major_city_city__old";
CREATE TABLE "major_city_city" (
    "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
    "name" varchar(30) NOT NULL,
    "designated" date NOT NULL,
    "area" decimal NOT NULL,
    "population" integer NOT NULL,
    "prefecture_id" integer NOT NULL REFERENCES "major_city_prefecture" ("id")
);
INSERT INTO "major_city_city" (
    "id",
    "name",
    "designated",
    "area",
    "population",
    "prefecture_id"
) SELECT "id", "name", "designated", "area", "population", NULL FROM "major_city_city__old";
DROP TABLE "major_city_city__old";
CREATE INDEX "major_city_prefecture_a34a99d3" ON "major_city_prefecture" ("district_id");
CREATE INDEX "major_city_city_71a71d54" ON "major_city_city" ("prefecture_id");
COMMIT;

若干回りくどい処理をしていますが、これはモデル名のアルファベット順に処理をしようとしているからでしょうか。

最後はマイグレーションファイルを適用するのを忘れずに。

$ ./manage.py migrate


いよいよ初期データ投入です。初期データは major_city/fixtures フォルダの中に JSON 形式か XML 形式で入れておきます(PyYAML をインストールすることで YAML 形式も使用可能)。

今回は JSON を利用しようと思います。長いです、ハイ。


2012年4月1日現在の政令指定都市のデータ

initial_data.json という名前にしているのは、古いバージョンで使えた syncdb コマンドに自動的に認識してもらうためのものでしたが、現在は migrate + loaddata が基本です。

$ ./manage.py loaddata initial_data

で初期データが投入できます。

次回は汎用ビューのお話。

*1:現状では警告を表示して CASCADE をデフォルトで設定している