DRF Serializer UML

上周要求对当前项目产出数据结构 UML 图及流程图, 为了完成流程图我花了一天的时间, 几近抓狂。 做完准备画数据结构关系图时, 心态炸裂。 于是上网搜索 Django 自动生成 Models UML 图的项目。

没想到还真有, 那就是著名的 django-extensions [1]

具体的生成 Models Graph 的方式, 请参考 Graph Models.

由于项目 Models 不能完全提现项目的全部数据结构, 要求对 Django rest framework 的 Serializer 也通过图描述出来。 我在网上并没有找到对 Serializer 进行构图的工具。

于是查看 django-extensions [1] graph_models 源码, 做了一个关于生成 Serializer UML 构图的工具。

思路基本上是通过 Serializer 入口, 解析每一个字段类型, 然后通过模板生成 dot [2] 文件。 然后通过 Graphviz [3] 工具提供的 dot 命令进行生成 png 图形。

解析代码

import os
from django.core.management.base import BaseCommand, CommandError
from django.template import Template, Context, loader
from rest_framework.serializers import Serializer, Field, ListField, ListSerializer, ChoiceField
from xxxxx.xxxxx.describe import DescribeSerializer  # Serializer 入口文件


serializers = {}

def name(serializer):
    try:
        return str(serializer.__name__)
    except:
        return str(serializer.__class__.__name__)

def render(serializer, label=None):
    if not label:
        label = getattr(serializer, '__doc__', '')

    if isinstance(serializer, ChoiceField):
        data = { 'name': label, 'label': 'Enum' }
        fields = []
        for k, v in serializer.choices.items():
            fields.append({'key': k, 'ref': 'field', 'type': type(v).__name__, 'label': v})
        
        data["fields"] = fields

        serializers[label] = data
        return 

    data = { 'name': name(serializer), 'label': label }
    fields = []

    ref_fields = []

    if hasattr(serializer, 'Meta'):
        ref_fields  = getattr(serializer.Meta, 'ref_fields', [])
        
    for k, field in serializer._declared_fields.items():
        ref_fields.append({
            'key': k, 'field': field
        })

    for item in ref_fields:
        k, field = item["key"], item["field"]
        if isinstance(field, Serializer):
            render(field)
            fields.append({'key': k, 'ref': 'ref', 'type': name(field), 'label': field.label})
            continue
        if isinstance(field, ListField):
            render(field.child)
            fields.append({'key': k, 'ref': 'ref-[]', 'type': name(field.child), 'list': name(field), 'label': field.label})
            continue
        if isinstance(field, ListSerializer):
            render(field.child)
            fields.append({'key': k, 'ref': 'ref-[]', 'type': name(field.child), 'list': name(field), 'label': field.label})
            continue
        if isinstance(field, ChoiceField):
            label = f'{k.title()}Choices'
            render(field, label)
            fields.append({'key': k, 'ref': 'ref', 'type': label, 'label': name(field)})
            continue
        else:
            fields.append({'key': k, 'ref': 'field', 'type': name(field), 'label': field.label})

    data["fields"] = fields

    serializers[name(serializer)] = data
<\>Python

渲染 dot 文件

模板文件 (dot/serializers.dot), 可以放到 templates , 通过 loader.get_template 获取, 也可以读取单独的文件内容, 然后通过 Template 类实例化.

import os

if __name__ == '__main__':
    render(DescribeSerializer) # 传入入口文件, 您可以指定您的入口文件, 当然您也可以改造上面的脚本让它支持多个入口.

    t = loader.get_template('dot/serializers.dot')

    output_file = './doc/dot/serializers.dot'  # 输出文件.

    if not os.path.exists(os.path.dirname(output_file)):
        raise CommandError('./doc/dot 不存在, 请创建.')

    with open(output_file, 'w') as f:
        f.write(t.render({'serializers': serializers}))
<\>Python

生成图形

通过 Graphviz 提供的 dot 命令.

首先您要安装 Graphviz 软件。然后执行

dot ./doc/dot/serializers.dot -T png -o ./doc/dot/serializers.png
<\>text

通过在线转换网站

如果您不想安装这个过于重量级的应用程序, 幸运的是我发现一个良心的转换网站。它免费!

请点击这里进入在线转换页面 dot-to-png

上传文件就可以生成 png 图形了。

#UML

引用


𝔔