讀了 Thinker: Dia 除了畫圖 一文,我也在(上一個)case裡用Dia來畫資料庫的草圖。而那時的資料庫是用Sqlalchemy,因此我改寫了Thinker的版本,以輸出Sqlalchemy的草圖為目標。但是,或因我程式碼寫的不好,或因Sqlalchemy的架構需要留心,生出來的程式碼你得知道在幹麻才能用,好吧,廢言。程式如下:


# This is a dia plug-in, you can put it in $HOME/.dia/python directory to
# use Export as SQLAlchemy create_all prototype.
import dia

class Table(object):
def __init__(self, name):
super(Table, self).__init__()
self.name = name
self.attrs = []

def add_attr(self, name, type):
self.attrs.append((name, type))

def expend_type(self, att_types):
types_arg = types_kwarg = ''
att_types = att_types.split(' ')
for t_ori in att_types:
t = t_ori.lower()
print t
if t == 'int':
types_arg += ', types.Integer'
elif t == 'text':
types_arg += ', types.Unicode'
elif t == 'unique':
types_arg += ', unique=True'
elif t == 'primarykey':
types_kwarg += ', primary_key=True'
elif t == 'notnull':
types_kwarg += ', nullable=False'
else:
types_arg += ', ' + t_ori # return the original attr value
types_arg += types_kwarg # add keywords attributes in the end
return types_arg

def __str__(self):
table_name = self.name.lower()
attr_strs = [ "Column('%s'%s)" % (name, self.expend_type(type))
for name, type in self.attrs ]
return "%s = Table( '%s', meta,\n %s\n)" % (
table_name, table_name, ',\n '.join(attr_strs))

class Klass(object):
def __init__(self, name):
super(Klass, self).__init__()
self.name = name
self.attrs = []

def add_attr(self, attrname):
self.attrs.append(attrname)

def __str__(self):
klass_name = self.name
table_name = self.name.lower()
attr_names = [ ", %s" % (name) for name in self.attrs
if name != ('id') ]
attr_strs = [ "self.%s = %s" % (name, name) for name in self.attrs
if name != ('id') ]
return "class %s(object):\n def __init__(self%s):\n %s" % (
klass_name, ''.join(attr_names),
'\n '.join(attr_strs))

class DiaSqla(object):
def __init__(self):
self.tables = []
self.klasses = []
self.cnames = []
self.filename = ''

def begin_render(self, data, filename):
self.filename = filename
first_layer = data.layers[0]
for obj in first_layer.objects:
if obj.type.name != 'UML - Class':
continue
cname = obj.properties['name'].value
attrs = obj.properties['attributes'].value
a_table = Table(cname)
a_klass = Klass(cname)
for attr in attrs:
n, v = attr[:2]
a_klass.add_attr(n)
a_table.add_attr(n, v)
self.tables.append(a_table)
self.klasses.append(a_klass)
self.cnames.append(cname)

def end_render(self):
fo = file(self.filename, 'w+')
fo.write('# Generated by DiaSqla exporter for SQLAlchemy\n\n')
for a_table in self.tables:
fo.write(str(a_table) + '\n\n')
for a_klass in self.klasses:
fo.write(str(a_klass) + '\n\n')
for cname in self.cnames:
fo.write('mapper('+ cname +', '+ cname.lower() +')\n')
fo.close()

dia.register_export('PyDia SQLAlchemy', 'sqla', DiaSqla())
把像這樣的圖:

輸出為:

# Generated by DiaSqla exporter for SQLAlchemy

model = Table( 'model', meta,
Column('id', types.Integer, primary_key=True),
Column('name', types.Unicode, unique=True, nullable=False),
Column('category_id', types.Integer, ForeignKey('category.id')),
Column('price', types.Integer)
)

stock = Table( 'stock', meta,
Column('model_id', types.Integer, ForeignKey('model.id'), primary_key=True),
Column('location_id', types.Integer, ForeignKey('node.id'), primary_key=True),
Column('available', types.Integer),
Column('reserved', types.Integer)
)

node = Table( 'node', meta,
Column('id', types.Integer, primary_key=True),
Column('name', types.Unicode, unique=True, nullable=False),
Column('address', types.Unicode),
Column('phone', types.Unicode),
Column('email', types.Unicode)
)

category = Table( 'category', meta,
Column('id', types.Integer, primary_key=True),
Column('parent_id', types.Integer),
Column('name', types.Unicode, unique=True, nullable=False)
)

class Model(object):
def __init__(self, name, category_id, price):
self.name = name
self.category_id = category_id
self.price = price

class Stock(object):
def __init__(self, model_id, location_id, available, reserved):
self.model_id = model_id
self.location_id = location_id
self.available = available
self.reserved = reserved

class Node(object):
def __init__(self, name, address, phone, email):
self.name = name
self.address = address
self.phone = phone
self.email = email

class Category(object):
def __init__(self, parent_id, name):
self.parent_id = parent_id
self.name = name

mapper(Model, model)
mapper(Stock, stock)
mapper(Node, node)
mapper(Category, category)
PS: 程式碼在這裡的CSS中,只有難看而已,如有意請貼到您最愛的編輯器上品賞(最好別是剛吃完飯,我可提醒了)。