django项目数据库设计文档生成
直接上代码
使用说明
按照步骤 TODO1、TODO2、TODO3 之后运行文件,数据库设计文档生成在当前目录下
# Desc : django项目生成doc文档
# TODO 注: model的Meta属性和字段属性记得添加verbose_name属性,apps.py需要添加verbose_name
# TODO 1. 复制项目DJANGO_SETTINGS_MODULE的值
# TODO 2. 在项目settings.py中配置 PROJECT_NAME(项目名称)
# TODO 3. 需要修改此自建app的列表 model_doc_apps的值
import os
import sys
import django
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# TODO 1 复制项目DJANGO_SETTINGS_MODULE的值 替换 radio_station_design.settings
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "radio_station_design.settings")
django.setup()
# TODO 3 需要导出的model模块
model_doc_apps = ['usermanage', 'station', ]
from django.apps import apps
# TODO 2 导入PROJECT_NAME(自定义项目名称)
from radio_station_design.settings import PROJECT_NAME
from docx import Document
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Inches, Pt, RGBColor
from datetime import datetime
class MyDoc:
"""生成docx文档"""
def __init__(self, out_path, project_name='项目名称'):
self.project_name = project_name
self.out_path = out_path
self._get_doc()
def _get_doc(self):
self.document = Document()
def add_heading(self, head, level=1):
"""自定义的标题格式"""
# heading = self.document.add_heading(head, level)
if not 0 <= level <= 9:
raise ValueError("level must be in range 0-9, got %d" % level)
style = "Title" if level == 0 else "Heading %d" % level
p = self.document.add_paragraph(style=style)
space_size = 11 - level
# 段前间距
p.paragraph_format.space_before = Pt(space_size)
# 段后间距
p.paragraph_format.space_after = Pt(space_size)
p_run = p.add_run(head)
p_run.font.name = u"仿宋"
if level:
p_run.font.color.rgb = RGBColor(0, 68, 136)
# 加粗
p_run.bold = True
# 字体大小
font_size = 28 - level * 2
p_run.font.size = Pt(font_size)
return p
def set_title(self):
"""设置标题以及生成日期"""
title = self.document.add_heading(self.project_name + "数据库设计", 0)
title.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
p = self.document.add_paragraph()
p.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT
# 右侧缩进
p.paragraph_format.right_indent = Pt(4)
pp = p.add_run("文档生成时间:" + datetime.now().strftime("%Y-%m-%d"))
pp.font.size = Pt(16)
def generate_doc(self):
"""生成文档固定部分"""
# 生成doc文档
self.set_title()
self.add_heading('引言', level=1)
self.add_paragraph("")
self.add_heading('关于', level=1)
self.add_paragraph(f"此文档主要介绍{self.project_name}数据库定义。")
self.add_heading('目标读者', level=1)
self.add_paragraph("此文档提供给软件开发人员和系统维护人员使用。")
self.add_heading('术语定义', level=1)
self.add_paragraph("")
self.add_heading('参考资料', level=1)
self.add_paragraph("")
self.add_heading('数据库设计', level=1)
def add_model(self, model_name='model名称', table_name="table_name", data=[]):
"""添加model生成doc部分"""
self.add_heading(model_name, level=3)
self.add_paragraph("表名:" + table_name)
if len(data) == 0:
return
table = self.document.add_table(rows=1, cols=7, style='Medium List 1 Accent 1')
table.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
table.autofit = True
header_cells = table.rows[0].cells
table.rows[0].height = Inches(0.5)
header_cells[0].text = '字段名称'
header_cells[1].text = '中文含义'
header_cells[2].text = '数据类型'
header_cells[3].text = '不为空'
header_cells[4].text = '默认值'
header_cells[5].text = '键值'
header_cells[6].text = '备注'
header_cells[0].width = Inches(1.5)
header_cells[1].width = Inches(1.5)
header_cells[2].width = Inches(1.25)
header_cells[3].width = Inches(0.75)
header_cells[4].width = Inches(1)
header_cells[5].width = Inches(0.8)
header_cells[6].width = Inches(2)
for d0, d1, d2, d3, d4, d5, d6 in data:
row = table.add_row()
row.height = Inches(0.3)
row_cells = row.cells
row_cells[0].text = str(d0)
row_cells[1].text = str(d1)
row_cells[2].text = str(d2)
row_cells[3].text = str(d3)
row_cells[4].text = str(d4)
row_cells[5].text = str(d5)
row_cells[6].text = str(d6)
self.set_cells_border(table)
self.set_cells_text_alignment(table)
self.add_paragraph("")
def set_cells_text_alignment(self, table):
"""给table设置居中格式或其他"""
for row in range(len(table.rows)):
for col in range(len(table.columns)):
# 水平居中
# table.cell(row, col).paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
# 垂直居中
table.cell(row, col).vertical_alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
def set_cell_value(self, cell, value):
"""给单个cell赋值,以及格式"""
cell.text = str(value)
# 水平居中
# cell.paragraphs[0].alignment=WD_PARAGRAPH_ALIGNMENT.CENTER
# 垂直居中
cell.vertical_alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
pass
def add_app_name(self, app_name='模块名称'):
"""添加模块名称"""
self.add_heading(app_name, level=2)
def _close_doc(self):
self.document.save(self.out_path)
def make_doc(self):
self._close_doc()
def add_paragraph(self, text):
"""添加段落以及设置格式"""
p = self.document.add_paragraph()
# 左侧缩进
p.paragraph_format.left_indent = Pt(4)
# 右侧缩进
p.paragraph_format.right_indent = Pt(4)
# 首行缩进
# p.paragraph_format.first_line_indent = Inches(0.25)
# 行间距
p.paragraph_format.line_spacing = Pt(18)
# 段前间距
p.paragraph_format.space_before = Pt(7)
# 段后间距
p.paragraph_format.space_after = Pt(15)
p_run = p.add_run(text)
p_run.font.name = u"仿宋"
p_run.font.size = Pt(14)
# 加粗
# p.add_run('bold').bold = True
# p.add_run(' and some ')
# 斜体
# p.add_run('italic.').italic = True
def add_picture(self, pic_path):
self.document.add_picture('monty-truth.png', width=Inches(1.25))
def add_table(self, data, rows, cols):
table = self.document.add_table(rows=rows, cols=cols)
header_cells = table.rows[0].cells
header_cells[0].text = 'Qty'
header_cells[1].text = 'Id'
header_cells[2].text = 'Desc'
for qty, id, desc in data:
row_cells = table.add_row().cells
row_cells[0].text = str(qty)
row_cells[1].text = id
row_cells[2].text = desc
def set_cells_border(self, table):
"""给table设置边框"""
for row in range(len(table.rows)):
for col in range(len(table.columns)):
self.set_cell_border(table.cell(row, col),
top={"sz": 0.5, "val": "single", "color": "#000000", "space": "0"},
bottom={"sz": 0.5, "val": "single", "color": "#000000", "space": "0"},
left={"sz": 0.5, "val": "single", "color": "#000000", "space": "0"},
right={"sz": 0.5, "val": "single", "color": "#000000", "space": "0"},
insideH={"sz": 0.5, "val": "single", "color": "#000000", "space": "0"},
end={"sz": 0.5, "val": "single", "color": "#000000", "space": "0"})
def set_cell_border(self, cell, **kwargs):
"""
Set cell`s border 设置单个cell的边框
参考文档:http://www.360doc.com/content/22/0204/21/76948455_1015984457.shtml
Usage:
set_cell_border(
cell,
top={"sz": 12, "val": "single", "color": "#FF0000", "space": "0"},
bottom={"sz": 12, "color": "#00FF00", "val": "single"},
left={"sz": 24, "val": "dashed", "shadow": "true"},
right={"sz": 12, "val": "dashed"},
)
"""
tc = cell._tc
tcPr = tc.get_or_add_tcPr()
# check for tag existnace, if none found, then create one
tcBorders = tcPr.first_child_found_in("w:tcBorders")
if tcBorders is None:
tcBorders = OxmlElement('w:tcBorders')
tcPr.append(tcBorders)
# list over all available tags
for edge in ('left', 'top', 'right', 'bottom', 'insideH', 'insideV'):
edge_data = kwargs.get(edge)
if edge_data:
tag = 'w:{}'.format(edge)
# check for tag existnace, if none found, then create one
element = tcBorders.find(qn(tag))
if element is None:
element = OxmlElement(tag)
tcBorders.append(element)
# looks like order of attributes is important
for key in ["sz", "val", "color", "space", "shadow"]:
if key in edge_data:
element.set(qn('w:{}'.format(key)), str(edge_data[key]))
def magic_doc():
mydoc = MyDoc(project_name=PROJECT_NAME, out_path=f'./{PROJECT_NAME}_数据库设计.docx')
mydoc.generate_doc()
for label in model_doc_apps:
app = apps.get_app_config(label)
mydoc.add_app_name(app.verbose_name)
for model_name in app.models:
target_cls = apps.get_registered_model(label, model_name)
model_key = target_cls._meta.verbose_name
table_name = target_cls._meta.db_table
fields = dict()
for field in target_cls._meta.fields:
if type(field).__name__ == 'ForeignKey':
f_name = field.name + "_id"
else:
f_name = field.name
if f_name not in fields.keys():
fields[f_name] = dict()
fields[f_name].update(field.__dict__)
fields[f_name]['field_type'] = str(type(field).__name__)
data_list = []
for (k, v) in fields.items():
is_main_key = is_for_key = False
if 'NOT_PROVIDED' in str(v['default']):
v['default'] = ''
if v['choices'] == None:
v['choices'] = ''
if v['primary_key'] is True:
is_main_key = True
if v['field_type'] == 'ForeignKey':
is_for_key = True
key_types = list()
if is_main_key:
key_types.append("主键")
if is_for_key:
key_types.append("外键")
v['primary_key'] = ','.join(key_types)
args = list()
for tag in ['name', 'verbose_name', 'field_type', 'null', 'default', 'primary_key', 'choices']:
if tag == 'choices':
if v[tag]:
args.append(str({item[0]: item[1] for item in v[tag]}))
else:
args.append("")
elif tag == 'null':
if v[tag]:
args.append(str(v[tag]))
else:
args.append("")
else:
args.append(str(v[tag]))
data_list.append(args)
mydoc.add_model(model_key, table_name, data_list)
mydoc.make_doc()
if __name__ == '__main__':
magic_doc()