LoginSignup
3
6

More than 5 years have passed since last update.

Djangoフレームワークにおけるmigrations機能の学習ノート(1)

Last updated at Posted at 2017-10-24

Djangoフレームワークを利用してウェブアプリケーションを作るのは大変便利です。MVCモデルを例えるとすれば、ModelとViewの部分で煩雑なディテールを実装してくれて、プログラマーがやりたいこと(Control)に集中できるように手助けしています。
今回では、Modelの部分に関連するmigrationsに関して記載したいと思います。DjangoのProjectの作成設定周りはDjangoオフィシャルチュートリアルを参考し、migrationsあたりはこちらを参考しています。

migrationsとは

日本語に翻訳すると、migrationsは「移行」を意味しています。Djangoでは、主にユーザーが設計したデータ構造を自動的にデータベースのスキーマに変更する機能を指しています(マニュアルでmigrationsファイルを書くことも可能ですが、機会があればまた紹介します)。

まず使ってみましょう

チュートリアルを参考しながら、アプリを作りましょう(ただし、データモデル部分だけ)。

開発環境
Mac OS:Sierra
python2.7.10
django1.11.2
mysql5.7.18

workingディレクトリに入って、ターミナルで下記コマンドを入力:

django-admin startproject m_Migration
python manage.py startapp polls

これでプロジェクトm_Migration及び中にpollsアプリを作成しました。
次にデータベースを設定します。デフォルトのsqliteではなく、mysqlを使用しますので、
プロジェクトをsettingsファイルを変更する必要があります。

Djangoのデータベースの設定

  1. mysqlを立ち上げ、ユーザーとユーザーのパスワードと今回使用するデータベースを作成します。
  2. 1で作成した属性に対応して、プロジェクトパッケージにあるsettings.pyを変更する(詳細はこちら参照)。
python:settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME':'blog',
        'USER':'user',
        'PASSWORD':'',
        'HOST':'',
        'PORT':'3306',
    }
}

あとはターミナルで初期設定を反映します。

python manage.py migrate

ログ:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying sessions.0001_initial... OK

mysqlクライアントに入り、データベースblog現在持ってるテーブルを見てみると:

+----------------------------+
| Tables_in_blog             |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
+----------------------------+

フレームワーク関連のデータベースが作成されました。
現時点model.pyモジュールを持ってるのはpollsアプリだけなので、pollsパッケージでmigrationsフォルダーが作成されされました。

モデルを作成しましょう

pollsアプリで使うデータモデルCategoryとArticleを作成します。

models.py

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models

# Create your models here.
class Category(models.Model):
    name = models.CharField(max_length=100)


class Article(models.Model):
    title = models.CharField(max_length=100)
    text = models.CharField(max_length=1000)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)

pollsアプリの機能は保存してあるブログの記事を表示することです。したがって、記事モデルArticleと記事のジャンルを示すCategoryモデルを作成しました。作成したモデルを反映する為に、まずオペレーションを記述したmigrationsファイルを作成します。

python manage.py makemigrations polls

ログ:

Migrations for 'polls':
  polls/migrations/0001_initial.py
    - Create model Article
    - Create model Category
    - Add field category to article

ここで、migrations/フォルダーでは今回model.pyへの変更を記録したmigrationsファイル0001_initial.pyを作成しました。
次に、作成したmigrationsファイルを反映させます

python manage.py migrate

ログ:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Applying polls.0001_initial... OK

今データベースでは

+----------------------------+
| Tables_in_blog             |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
| polls_article              |
| polls_category             |
+----------------------------+

テーブルのスキーマを見てみると:

mysql>DESC polls_article;
+-------------+---------------+------+-----+---------+----------------+
| Field       | Type          | Null | Key | Default | Extra          |
+-------------+---------------+------+-----+---------+----------------+
| id          | int(11)       | NO   | PRI | NULL    | auto_increment |
| title       | varchar(100)  | NO   |     | NULL    |                |
| text        | varchar(1000) | NO   |     | NULL    |                |
| category_id | int(11)       | NO   | MUL | NULL    |                |
+-------------+---------------+------+-----+---------+----------------+
mysql>DESC polls_category;
+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(11)      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(100) | NO   |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+

モデル通りに、新たにpollsアプリのテーブル(polls_article、polls_category)が作成されました(idフィールドはデフォルトで自動作成されます)。ここまでが、アプリでモデルを設計し、djangoのmigrations機能を利用してデータベースを作成する流れです。

よくある利用ケース

一度作成してから、新機能の追加など、データモデルを修正する場合もあります。

データモデルの更新

pollsアプリを例とします。例えば、もともとのブログサービスでは文字だけの記事を提供しましたが、いいデジカメを購入したので、up主が記事に写真をつけたいと思うようになりました。ここで、Articleモデルに写真のurlを保存するフィールドimage_urlを追加しましょう(すでにあるレコードのためにデフォルト値も設定します)。

models.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models

# Create your models here.
class Category(models.Model):
    name = models.CharField(max_length=100)


class Article(models.Model):
    title = models.CharField(max_length=100)
    text = models.CharField(max_length=1000)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    image_url = models.URLField(max_length=200, default='toBeImplement')

変更を記録しましょう

python manage.py makemigrations polls

ログ:

Migrations for 'polls':
  polls/migrations/0002_article_image_url.py
    - Add field image_url to article

新しいmigrationsファイル0002_article_image_url.pyが作成されました。

変更を反映させます。

 python manage.py migrate

データベース現在のスキーマを見て見ましょう:

DESC polls_article;
+-------------+---------------+------+-----+---------+----------------+
| Field       | Type          | Null | Key | Default | Extra          |
+-------------+---------------+------+-----+---------+----------------+
| id          | int(11)       | NO   | PRI | NULL    | auto_increment |
| title       | varchar(100)  | NO   |     | NULL    |                |
| text        | varchar(1000) | NO   |     | NULL    |                |
| category_id | int(11)       | NO   | MUL | NULL    |                |
| image_url   | varchar(200)  | NO   |     | NULL    |                |
+-------------+---------------+------+-----+---------+----------------+

計画通りに、データベースを更新できました。

データモデルの復旧

gitなどVCSのように、データモデルのバージョンをコントロールすることもできます。この機能の活躍場面として、緊急事態でのデータモデルの復旧が挙げられます。
仮に上記の例で写真urlに関連する変更を行い、migrationsファイル0002_article_image_url.pyが作成された状態でmigrateコマンドを実行しました。

 python manage.py migrate

ここでmigrateが失敗しました。最新のmigrationsの状態が0002_article_image_url.pyであり、しかし実際データベースのスキーマは0001_initial.pyに対応しています。この状態ではテーブルに写真urlに対応するコラムがありません。pollsアプリから写真urlデータをアクセスしようとしたらエラーが出ます。この問題の1つの対応策として、migrationsの状態を0001_initial.pyに復旧するが挙げられます(それからバッグを探します)。

復旧するために、同じくmigrateコマンドを利用して、migrationsファイル0001_initial.pyが記録した状態まで復旧します。

python manage.py migrate polls 0001_initial

ログ:

Operations to perform:
  Target specific migration: 0001_initial, from polls
Running migrations:
  Rendering model states... DONE
  Unapplying polls.0002_article_image_url... OK

ログが示したように、0002_article_image_url.pyの変更が撤廃(unapply)されました。今のArticleのテーブルのスキーマを見て見ましょう:

DESC polls_article;
+-------------+---------------+------+-----+---------+----------------+
| Field       | Type          | Null | Key | Default | Extra          |
+-------------+---------------+------+-----+---------+----------------+
| id          | int(11)       | NO   | PRI | NULL    | auto_increment |
| title       | varchar(100)  | NO   |     | NULL    |                |
| text        | varchar(1000) | NO   |     | NULL    |                |
| category_id | int(11)       | NO   | MUL | NULL    |                |
+-------------+---------------+------+-----+---------+----------------+

テーブルが元の状態に戻りました。

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6