Python
Django
DjangoDay 5

Djangoのモデル定義をreSTやMarkdownで出力したい

概要

  • https://github.com/tell-k/django-modelsdoc
  • Djangoのモデル定義一覧を reStrucutedText や Markdownで出力できるライブラリを作って公開しています。
  • 社内で使ってくれてる人が1名だけ確認できたので、つらつら書いてみようと思います。

サンプル

例えば下記URLのようなモデルあるとします

# refs https://github.com/tell-k/django-modelsdoc/blob/master/tests/models.py

from django.db import models
from django.contrib.auth.models import User

class Poll(models.Model):
    """ Poll

    * Poll has question and description fields
    """

    question = models.CharField('Question Name', max_length=255)
    description = models.TextField('Description', blank=True)
    """ Description field allows Blank """

    null_field = models.CharField('Null Test', null=True, max_length=255)
    blank_field = models.CharField('Blank Test', blank=True, max_length=255)
    both_field = models.CharField('Both Test',
                                  null=True, blank=True, max_length=255)
    index_field = models.CharField('Index Test', db_index=True, max_length=255)

    class Meta:
        verbose_name = 'Poll'

listing_models のコマンドを叩くと 下記のようなモデル定義がreSTやMarkdownのテキストとして手に入ります。

全体的にどうなるかは下記のサンプルファイルをご覧ください。

動機

作った動機は以下のような感じです。

  • Djangoプロジェクトの引き継ぎ時にモデルの説明する時に一覧があると良い
  • schema2rst という素晴らしいライブラリを使っていた。

  • But, Djangoのマイグレーション機能ではカラム/テーブルコメントとかをDBには反映できない

  • つまりカラムに対する注意書きや、カラムにどんな値が入るかとかを出力できない。

もう面倒なのでDBからではなくDjangoのモデル定義を取ってきていい感じに出力しようと思った次第

工夫したところ

Fieldのdocstringを抽出できるようにした

  • 上記にも説明した通り DBのテーブル/カラムコメント に対応する データが DjangoのFieldには存在しない。
  • help_text というものがあるが、これは編集画面等で提示する文章なので、開発者向けの説明をいれる場所ではない。
  • なので Field定義のdocstring を拾って出力できるようにしました。
description = models.TextField('Description', blank=True)
""" Description field allows Blank """  # <- この文書をコメント列として出力する
  • 上記のような docstring は Sphinxの中にある sphinx.pycode.ModuleAnalyzer というクラスを利用すると抽出することが可能なので利用しました。 (ミニマムに再実装したかったけど結構なヘビーな感じですぐにはできなそうだったので断念)

ChoicesやForeignKeyの情報も表示できるようにした

  • ChociesやForeignKeyがFieldに設定されていればそれをよしなに表示できるようにしました。

使い方

基本的な使い方はREDMEをみてもらうとして、settings.py でカスタマイズできる内容を紹介します。

出力対象のアプリを絞りたい

  • 例えば、特定のアプリのモデルだけを表示したいとかいう場合には MODELSDOC_APPS という設定を設定します。
  • 出力されるアプリの順番もこれで変えることが可能です。
  • 下記のように書くと、pollsアプリのモデルしか出力されません。
MODELSDOC_APPS = (polls,)

出力される列の順番/項目を変えたい

  • MODELSDOC_DISPLAY_FIELDS という設定を settings.py 書いてください。
  • デフォルトは以下のようになっています。
  • 順番と項目を定義できます。上に書いたものが左から列として出力されます。
# settings.py

MODELSDOC_DISPLAY_FIELDS = (
    ('Fullname', 'verbose_name'),
    ('Name', 'name'),
    ('Type', 'db_type'),
    ('PK', 'primary_key'),
    ('Unique', 'unique'),
    ('Index', 'db_index'),
    ('Null/Blank', 'null_blank'),
    ('Comment', 'comment'),
)
  • Django の Field オブジェクトが持っている属性値であれば、大概なんでもいけると思います。
  • 中身は getattr で該当の属性をひっぱているだけです。
getattr(field_obj, 'versbose_name')

モデルの制約やオプションの表示内容の変更

  • MODELSDOC_MODEL_OPTIONSを設定してください
  • ModelのMetaに設定しているような情報を引っ張って出力してくれます。
  • 例えば以下のような項目があります。
MODELSDOC_MODEL_OPTIONS = (
    'unique_together',
    'index_together',
    'ordering',
    'permissions',
    'get_latest_by',
    'order_with_respect_to',
    'db_tablespace',
    'abstract',
    'swappable',
    'select_on_save',
    'default_permissions',
    'default_related_name'
)

Djangoが自動で作るモデル群は除外したい

  • ManyToManyField で自動的に作られる中間テーブル用のモデル
  • Djangoが最初から用意してるモデル群
  • これらのモデル群は、自分で定義したわけではないので出力してほしくない場合もあります
  • Djangoは内部的にこれらのモデル群を「自動で作られるモデル」として判別しています。
  • それを利用して自動で作られたモデル群を除外しています。
  • 下記のように設定を False にしてもらえれば除外されて出力されます。
MODELSDOC_INCLUDE_AUTO_CREATED = False

ModelやFieldに追加で属性を加えたい

  • ModelやFieldのオブジェクトはそのまま使っているわけでなく、それぞれをラップするクラスを用意しています。
MODELSDOC_MODEL_WRAPPER = 'modelsdoc.wrappers.ModelWrapper'
MODELSDOC_FIELD_WRAPPER = 'modelsdoc.wrappers.FieldWrapper'
  • デフォルトでは上記のようになってますが、好きなクラスに差し替えることができます。
  • 例えば下記のようにカスタムクラスを作って設定すると、MODELSDOC_DISPLAY_FIELDSの項目として使えるようになります
from modelsdoc.wrappers import FieldWrapper

class MyFieldWrapper(FieldWrapper):

  @property
  def unique_ja(self):
      return '唯一無二項目' if self.unique else ''
MODELSDOC_FIELD_WRAPPER = 'path.to.MyFieldWrapper'

MODELSDOC_DISPLAY_FIELDS = (
    ('Fullname', 'verbose_name'),
    ('Name', 'name'),
    ('Type', 'db_type'),
    ('PK', 'primary_key'),
    ('Unique', 'unique'),
    ('ユニーク(日本語)', 'unique_ja'),  <- 追加
    ('Index', 'db_index'),
    ('Null/Blank', 'null_blank'),
    ('Comment', 'comment'),
)

テンプレートをカスタマイズしたい

modelsdoc/models.md
modelsdoc/models.rst
{% load modelsdoc_tags %}

{% emptylineless %}
{% if hoge %} <- ここの空行が消える
- hogehoge
{% endif %} <- ここの空行が消える
{% endemptylineless %}

今後の展望

  • ていう話をちゃんとドキュメント化したい
  • Django2.0 対応 -> 昨日終わった
  • コメントをDBに一括で反映するコマンドを用意したい
  • plantUMLでER図的なものを出力できるようにしたい。

以上おわり