14
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Djangoでテーブルのコメントを生成するコマンドを作ってみた

Last updated at Posted at 2014-12-12

はじめに

Djangoはmodels.pyに書いたModelから、自動的にCreate Table文を発行してテーブルを作ってくれるので便利…なんだけど、テーブルやカラムにコメントをつけてくれるところまでは対応していない。
作成されたテーブルからER図を逆生成したい場合なんかは、コメントがついててくれるとありがたいんだけどなぁ…

というわけで、無いなら自分で作るか、と試行錯誤してみた。

テーブルコメント・カラムコメントの構文

テーブルコメント、あるいはカラムコメント追加の構文は↓の通り。

comment on table table_name is 'comment'
comment on column table_name.column_name is 'comment'

ただし、これはOracleとPostgreSQL(とDB2)の独自仕様で、SQL標準ではないらしい。
できればsqlite3も同じ構文で作れればよかったんだけど、無理みたいなんで今回は断念。
最低限、普段よく使ってるOracleでコメントつけれればいいや、と割りきることにする。

Djangoカスタムコマンドの作成

普通にPythonモジュールとして作ってもいいが、せっかくなのでDjangoのコマンドを自作して、コマンドラインからサクッと実行できるようにする。
Djangoのコマンドなら、AppCommandクラスを継承することで、引数にDjangoアプリケーションのAppConfigオブジェクトを受け取れるので、コメント作成対象のModel一覧を取得するのも楽。

Djangoカスタムコマンドの作成については、以下の記事を参考にさせてもらった。

さらに、django.core.manegement.commands.sqlsequenceresetのソースを参考にした。

コマンドのフォーマット

コマンド名は「createtablecomment」としておく。フォーマットは、↓のような感じにする。

manage.py createtablecomment [options] <app_label app_label ...>

コマンド用のモジュール配置

今回作るコマンドは特定アプリ用のものではないので、プロジェクトフォルダにモジュールを配置する。(以下は配置の参考。プロジェクト名は「myproject」とする)

myproject
 └ myproject
   └ management
     └ commands
       └ createtablecomment.py

コマンドクラスのソース

handle_app_configの中身以外は、sqlsequencereset.pyをコピーしてほぼそのまま使ってます。

createtablecomment.py
from __future__ import unicode_literals

from optparse import make_option

from django.core.management.base import AppCommand
from django.core.management.sql import check_for_migrations
from django.db import connections, DEFAULT_DB_ALIAS
from django.db.models.fields.related import ForeignKey

class Command(AppCommand):
    help = 'Prints the SQL statements for create comment of tables and columns for the given app name(s).'

    option_list = AppCommand.option_list + (
        make_option('--database', action='store', dest='database',
            default=DEFAULT_DB_ALIAS, help='Nominates a database to print the '
                'SQL for.  Defaults to the "default" database.'),
    )

    output_transaction = True

    def handle_app_config(self, app_config, **options):
        if app_config.models_module is None:
            return
        connection = connections[options.get('database')]
        check_for_migrations(app_config, connection)
        models = app_config.get_models(include_auto_created=True)
        cursor = connection.cursor()
        statements = []

        for model in models :
            statement = "COMMENT ON TABLE %s IS '%s'" %
                      (model._meta.db_table, model._meta.verbose_name)
            statements.append(statement)
            cursor.execute(statement)
            for field in model._meta.fields :
                if isinstance(field, ForeignKey) :
                    column  = '%s_id' % field.name
                    comment = '%s(FK:%s)' %
                            (field.verbose_name, field.related.parent_model._meta.db_table)
                else :
                    column  = '%s' % field.name
                    comment = field.verbose_name
                statement = "COMMENT ON COLUMN %s.%s IS '%s'" %
                          (model._meta.db_table, column, comment)
                statements.append(statement)
                cursor.execute(statement)
        return '\n'.join(statements)

処理内容は↓のような感じ。

  1. app_config.get_models()でアプリ配下のmodelクラス一覧を取得、forで回す
  2. テーブル用のcreate comment文を生成&実行(connection.cursorl()でカーソル取得してcursor.execute(statement)
  3. model._meta.fieldsでmodelが持っているfieldの一覧を取得、forで回す
  4. カラム用のcreate comment文を作成&実行(ForeignKeyフィールドの場合は、カラム名に「_id」がつくので補完。また、コメントに「どのテーブルのFKか?」という情報を付加)
  5. 実行したcreate comment文の一覧を改行区切りの文字列にしてreturn(コンソールからコマンド実行した場合は、コンソールに実行されたSQL文が出力される)

実行結果

↓のmodels.pyを持つ、myappアプリを対象に実行してみた。

models.py
from django.db import models

class Author(models.Model):
    class Meta :
        db_table = 'AUTHORS'
        verbose_name = '著者'
        verbose_name_plural = verbose_name

    name = models.CharField('名前', max_length=50)
    birthday = models.DateField('生年月日')

class BooK(models.Model):
    class Meta:
        verbose_name = ''
        verbose_name_plural = verbose_name

    name = models.CharField('書籍名', max_length=100)
    price = models.IntegerField('値段')
    author = models.ForeignKey(Author, verbose_name='著者')

コマンド実行

> python manage.py createtablecomment myapp
BEGIN;
COMMENT ON TABLE AUTHORS IS '著者'
COMMENT ON COLUMN AUTHORS.id IS 'ID'
COMMENT ON COLUMN AUTHORS.name IS '名前'
COMMENT ON COLUMN AUTHORS.birthday IS '生年月日'
COMMENT ON TABLE myapp_book IS '本'
COMMENT ON COLUMN myapp_book.id IS 'ID'
COMMENT ON COLUMN myapp_book.name IS '書籍名'
COMMENT ON COLUMN myapp_book.price IS '値段'
COMMENT ON COLUMN myapp_book.author_id IS '著者(FK:AUTHORS)'

COMMIT;

無事、テーブルとカラムにコメントが作成された。

14
13
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
14
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?