小林ノエルのエンジニア的忘備録

フリーランス兼会社員エンジニアが技術とかリモートワークのこととかをツラツラ書いていきます

DjangoでCREATE DATABASEを自動化する(あるいはrake db:create的な何か)

問題意識

DjangoではRails同様migrationの仕組みがあるわけだが、Databaseの作成はマニュアル操作でやることを想定しているっぽい。

docs.djangoproject.com

Djangoデフォルトではsqliteのファイルが出来上がりそのDBを使うように設定されている。 この場合はpython manage.py migrateを実行すればそのままテーブルの作成が成功する(CREATE DATABSASEを手動で操作する必要はない)

しかしMySQLなどを使う場合はCREATE DATABASEなどを手動で実行しなければならない。

Railsにはrake db:createのようにDatabaseの作成もコマンドで自動でやってくれるので、Djangoでもそういうのしたかった。

解決策

今回はカスタムコマンド(Railsでいうところのrake taskのようなもの)として実装する方法をとってみた。

docs.djangoproject.com

記事にあるように、polls/management/commands/以下にcreatedb.pyのようなファイルを作る。なおpip moduleはDjango推奨のmysqlclientを使用する想定なので、pip install mysqlclientなどでインストールされていることが前提。

from django.core.management.base import BaseCommand, CommandError
import MySQLdb


class Command(BaseCommand):
    help = 'Create database'

    def handle(self, *args, **options):
        print('create database!')
        db = MySQLdb.connect(host='mysql', user='root', passwd='password1')
        cursor = db.cursor()
        sql = 'CREATE DATABASE IF NOT EXISTS `polls_development` DEFAULT CHARACTER SET `utf8mb4`'
        cursor.execute(sql)
        db.close()

IF NOT EXISTSが有効なmysqlバージョン限定となるが、これを付けることによりすでにDatabaseが存在する場合のエラーハンドリングを気にしなくてよくなる。また文字コードutf8mb4としておく。 Database名を**_developmentとしたのはRails的なあれを持ち込んでしまった(良いのか悪いのはわからない)。

ついでにDROP DATABASEのコマンドも用意しておく。

from django.core.management.base import BaseCommand, CommandError
import MySQLdb


class Command(BaseCommand):
    help = 'Drop database'

    def handle(self, *args, **options):
        print('Drop database!')
        db = MySQLdb.connect(host='mysql', user='root', passwd='password1')
        cursor = db.cursor()
        sql = 'DROP DATABASE IF EXISTS `veritas_development`'
        cursor.execute(sql)
        db.close()

これで以下のようなコマンドでDatabaseの作成と削除をコマンド1つでできるようになった。

$ python3 manage.py createdb

環境

  • OS: ubuntu20.04 (docker container)
  • Python 3.8.5
  • Django 3.1.7
  • mysqlclient 2.0.3