直接上代碼
按照步驟 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()