見出し画像

【77日目】Django_マイグレーションがやっとうまくいった話_プログラミング学び日記

 このnoteは、31歳未経験からエンジニアを目指して勉強していく記録を綴っているものです。現在はAdTechでカスタマーサクセスを担当しつつ、色んなチャンスに恵まれ、CS業務や子育てと並行しながらチャレンジしています。

 これからプログラミングを始める方にとってのTipsやモチベアップに繋げられるように頑張りたいと思っています。
--------------------------------------------

ついにやりました…!
既存のアプリに新しいモデル(DBのテーブル)を追加したり、既存のモデルに新しいフィールドを追加したりできました。

ここ数日、

django.db.utils.OperationalError: no such table: todo_todocategory

というエラーに悩まされましたが、原因も恐らく明らかにできたと思います。

ただ根本的な原因は、やはりマイグレーションの流れをきちんと理解できていなかったことにつきます。まずは以下の2つの動画でしっかり基礎を学ぶことで、解決策への道筋を得ました。


マイグレーションの流れ

マイグレーションは大きく以下の流れで実施します。
①models.pyを編集
②「python manage.py makemigrations アプリ名」でマイグレーションファイルを作成
③「python manage.py migrate」でマイグレーションを実行

②と③の間に一拍あるのがポイントです。つまりすぐに実行されるわけではなく、②はマイグレーションの実行内容が記載されたファイルを作成する手順になります。また、models.pyが編集されていなければ②は実行できません(何も変更は起きてないよ、というメッセージが出ます)。

マイグレーションファイルは以下のように採番されて作成されます。マイグレーション実行時は、番号が若い順番に実行されていきます。つまり、途中の処理で詰まれば後の処理は実行されません。

各ファイルが実行済みかどうかは、作成済みのマイグレーション一覧を表示する「python manage.py showmigrations アプリ名」というコマンドで分かります。

チェックボックスが[X]となっていれば実行済みで、[  ]であれば実行前です。ちなみに実行前であれば削除しても影響はありません。


models.pyを編集してモデルを追加

まず今回はTodoアプリの入力項目にカテゴリー(社内タスクなのか納品物なのか等)とステータス(作業中なのか完了済みなのか等)を増やしたいので、2つモデルを追加します。TodoCategoryとTodoStatusという名前にしました。

from django.db import models

class TodoCategory(models.Model):
    name = models.CharField(
        max_length=255,
        blank=False,
        null=False,
        unique=True,)

    def __str__(self):
        return self.name


class TodoStatus(models.Model):
    name = models.CharField(
        max_length=255,
        blank=False,
        null=False,
        unique=True,)

    def __str__(self):
        return self.name


class Todo(models.Model):
    title = models.CharFiield(
        "タスク名", 

 --- 以下省略 ---


メインであるTodoモデルにもフィールドを追加、、するのは後回し!!

次にメインのモデルにカテゴリーとステータスを追加したいところですが、これは後回しが必須です。書いてもOKですがコメントアウトしておきます。

class Todo(models.Model):
    title = models.CharFiield(
        "タスク名", 

    --- 中略 ---
    
    # 新しいフィールドを書いておいてもいいけど一旦コメントアウトする
    """
    category = models.ForeignKey(
        TodoCategory,
        on_delete=models.CASCADE,
        verbose_name="業務種別")
    
    status = models.ForeignKey(
        TodoStatus,
        on_delete=models.CASCADE,
        verbose_name="処理状況")
    """

    def __str__(self):
        return self.title

理由は2つあります(表裏一体ですが)。

  1. この段階ではTodoCategoryとTodoStatusのモデルが作成されていない

  2. ForeignKeyはデフォルト初期値が必要なフィールド

つまり、ForeignKeyでデフォルト初期値を設定しようとしても、参照先のデータベースがそもそも存在しない状態なので、この段階で「makemigrations」しようとするとエラーになります。

これが私がずっと直面していたエラーの正体だったみたいです。

解消方法は実にシンプルで、まずはモデルの追加に対してのみ②「makemigrations」→③「migrate」を実行。無事にモデルが追加されたら上記のコメントアウトを外して再び②→③を実行。これだけです。

モデルの追加には特別な処理が必要なのかと思ってましたが、Djangoはすごいもので、そんなことはありませんでした。新しいモデルを定義するだけで「CreateModel」というメソッドを実行してくれます。


デフォルト初期値の設定をDjangoから求められたら

少し躓いたポイントだったのでメモしておきます。既存アプリにForeignKeyのフィールドを追加すると、空っぽはダメなフィールドなのに、既存のレコードが空の状態になるため、デフォルト初期値の設定を求められます。

以下は失敗例です(「未設定」という名前は事前にTodoCategoryに登録したものです)。

You are trying to add a non-nullable field 'category' to todo without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option: 1
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> "未設定"

上記でmakemigrationsまでは正常っぽく動作しますが、migrateするときに以下のエラーが出ます。

ValueError: Field 'id' expected a number but got '未設定'.

ここで、モデルに書かれているのはどういうフィールドだったかおさらいします。

    category = models.ForeignKey(
        TodoCategory,
        on_delete=models.CASCADE,
        verbose_name="業務種別")

つまり、TodoCategoryのような別モデルからデータを引っ張る際は、そのidを元に引っ張っているみたいです。そのため"未設定"のように直接入力してしまうとダメで、"未設定"がTodoCategoryで持っている「id」の数字を入力する必要がありました。

"未設定"のidは「1」だったので、1を入力すると上手くいきました。


Todoアプリはどう変わった??

テンプレートも更新して今回の内容が表示されるようにしました。
まずは作成画面です。

更新前

更新後

業務種別(category)と処理状況(status)という項目が無事追加できました!!次に詳細画面です。

更新前

更新後

「_納品物」が業務種別で、「≪未対応≫」が処理状況です。
こちらも上手くいきましたね!


すごく様々なエラーに直面した取り組みでしたが、一つずつ解消していくことでたどり着けました。
地道に一歩ずつ進めば何とかなるものですね。

あーよかった。


参考


これまで修了したコース等

【YouTube_Django関係】
Pythonでウェブサービスを作ろう! #1
テンプレートをマスターしよう! #2
静的ファイルを配信しよう !#3
本番公開しよう! #4
データベースと接続しよう! #5
ブログを作って学ぶモデル入門! #6
これが汎用ビューの力! #7
データベースマイグレーション前編 #15
データベースマイグレーション後編 #16

【Paiza】
Aランクレベルアップメニュー 24/49問
ループメニュー1 20/20問
ループメニュー2 12/20問
配列メニュー      56/64問
条件分岐メニュー    25/25問
二重ループメニュー   19/19問
配列活用メニュー    26/26問
文字列処理メニュー   30/30問
Bランクレベルアップメニュー 62/62問
Cランクレベルアップメニュー 30/30問
ランクB合格
ランクC合格

【書籍/ブログ】
Django入門 | 初心者でも1時間でWebアプリ(Todoアプリ)を作成するコース
基礎からのMySQL     514/514頁
Web技術の基本      189/189頁 ※2周目中
京大のPython教科書    116/201頁
Pythonデータベースプログラミング 194/194頁
Pythonエンジニアファーストブック読了

【Progate】
Python Ⅰ~Ⅴ
Python アプリ版 コースⅠ~Ⅴ
SQL Ⅰ~ Ⅳ
SQL アプリ版 コースⅢ
HTML&CSS 初級編

【その他】
Pythonの環境構築
VSCodeの環境構築
MySQLの環境構築(MAMP)
Git / GitHubの環境構築
HEROKUの環境構築

この記事が気に入ったらサポートをしてみませんか?