Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
12
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

@44d

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

はじめに

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;

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
12
Help us understand the problem. What are the problem?