Python將數據庫的父子關系表畫成樹形結構


如何像下圖一樣將關系型數據庫的上下級關系畫成樹形圖

     

 

測試數據准備

 為了程序的通用性,也方便進行驗證,本例采用最通用的sql寫法,數據庫采用SQLiter3, 如果你的數據庫是ORACLE, MS-SQL, MYSQL,不用修改任何代碼,只需要在調用的時候傳入相應的db連接即可

def sampledata():
    db=sqlite3.connect('dbname.db')
    cur=db.cursor()
    cur.execute("create table if not exists relation(mother, child)");
    cur.execute("INSERT INTO relation(mother, child) VALUES('1000', '1100')");
    cur.execute("INSERT INTO relation(mother, child) VALUES('1000', '1200')");
    cur.execute("INSERT INTO relation(mother, child) VALUES('1000', '1300')");
    cur.execute("INSERT INTO relation(mother, child) VALUES('1000', '1400')");

    cur.execute("INSERT INTO relation(mother, child) VALUES('1200', '1210')");
    cur.execute("INSERT INTO relation(mother, child) VALUES('1200', '1220')");
    cur.execute("INSERT INTO relation(mother, child) VALUES('1200', '1230')");

    cur.execute("INSERT INTO relation(mother, child) VALUES('1220', '1221')");
    cur.execute("INSERT INTO relation(mother, child) VALUES('1220', '1222')");

    db.commit();

 

看起來是這樣的

 程序編寫

 節點信息類(相當於C/C++里面的結構體)

value就是當前節點的值

isleaf記錄的是當前節點是否是葉子節點

leafcounts記錄的是當前節點下面有多少個葉子節點,這個主要是為了在排版的時候知道占用多寬,

maxlevel記錄的是當前節點下面最深的深度是多少,這個主要是為了在排版的時候知道生成多長的圖片

class decisionnode:
    def __init__(self, value, isleaf=False, leafcounts=0, maxlevel=1):
        self.childs=[]
        self.value=value
        self.isleaf=isleaf
        self.leafcounts=leafcounts
        self.maxlevel=maxlevel
    
    def addchild(self, child):
        self.childs.append(child)

 關系樹類編寫

 gentree 方法用於生成整棵樹,需要傳值:db:數據庫連接,mathervalue:根節點的數值 tablename:數據庫表名 childcol:子節點在表中的對應的字段名稱 mothercol:上級節點在表中的字段名稱

 draweachnode 方法用於化每個節點的值和兩個節點之間的連線

 drawTree 方法用於畫整棵樹,它會調用draweachnode然后利用draweachnode的遞歸畫完整棵樹

class RelationTree:
    def __init__(self, basewidth=100, basedepth=100):
        self.basewidth = basewidth
        self.basedepth = basedepth
        self.root=None
        
    def gentree(self, db, mothervalue, tablename, childcol, mothercol):
        self.root=decisionnode(mothervalue)
        cur=db.cursor()
        def swap_gentree(node):
            cur.execute("select %s from %s where %s = '%s'" % \
                (childcol, tablename, mothercol, node.value));
            results=cur.fetchall()
            
            #如果是葉子節點,則直接返回
            if not results:
                return decisionnode(node.value, isleaf=True, maxlevel=1)
            
            #程序運行到這里,說明是非葉子節點
            #對非葉子節點進行其下包含的葉子節點進行統計(leafcounts)
            #該節點之下最深的深度maxlevel收集
            maxlevel=1
            for each in results:
                entrynode=swap_gentree(decisionnode(each[0]))
                if(entrynode.isleaf):
                    node.leafcounts += 1
                else:
                    node.leafcounts += entrynode.leafcounts
                
                if (entrynode.maxlevel > maxlevel):
                    maxlevel = entrynode.maxlevel
                node.addchild(entrynode)
            
            node.maxlevel = maxlevel+1
            return node
        swap_gentree(self.root)



    def draweachnode(self, tree, draw, x, y):
        draw.text((x,y), tree.value, (0,0,0))
        
        if not tree.childs:
            return
        
        childs_leafcounts=[child.leafcounts if child.leafcounts else 1 for child in tree.childs]

        leafpoint=x-sum(childs_leafcounts)*self.basewidth/2

        cumpoint=0
        for childtree, point in zip(tree.childs, childs_leafcounts):
            centerpoint=leafpoint+self.basewidth*cumpoint+self.basewidth*point/2
            cumpoint += point
            draw.line((x,y, centerpoint, y+self.basedepth), (255,0,0))
            self.draweachnode(childtree, draw, centerpoint, y+self.basedepth)
            

    def drawTree(self, filename='tree.jpg'):
        width=self.root.leafcounts * self.basewidth + self.basewidth
        depth=self.root.maxlevel * self.basedepth + self.basedepth
        img=Image.new(mode="RGB", size=(width, depth), color=(255,255,255))
        draw=ImageDraw.Draw(img)
        self.draweachnode(self.root, draw, width/2, 20)
        
        img.save(filename)

 

 測試驗收

確認運行代碼的環境是否已經安裝以下包

pillow (如果沒有請pip install pillow (這個模塊實際上就是PIL))

sqlite3 (如果沒有請pip install sqlite3)

 

新建一個文件 drawtree.py

在文件開頭導入需要的模塊

import sqlite3
from PIL import Image, ImageDraw

然后將上面的代碼復制進去

然后根據下面操作

 

 到這里,就會在工作目錄下生成tree.jpg了,效果就像文章開頭那樣,

為了程序的通用性,本例的程序已經寫成很通用的代碼了,如果你用的是其他數據庫

則不要用我的樣本數據,也就是不要運行drawtree.sampledata這個函數

然后將db=sqlite3.connect('daname,db')換成你數據庫對應的連接,例如你的數據庫是oracle,則用db=cx_Oracle.connect('usernae/password@tnsname')

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM