from django.conf import settings from django.db.models import Model , ForeignKey , ManyToManyField , OneToOneField def get_all_models (): all_models = set () for app in settings . INSTALLED_APPS : try : models = __import__ ( app + '.models' , {}, {}, 'models' ) except ImportError : continue for attr in dir ( models ): obj = getattr ( models , attr ) if isinstance ( obj , object ) and \ hasattr ( obj , '__bases__' ) and \ Model in obj . __bases__ : all_models . add ( obj ) return all_models def get_relns ( all_models ): foreignkeys = [] one_to_one = [] many_to_many = [] for model in all_models : for field in model . _meta . fields : if isinstance ( field , OneToOneField ): # must come before FK to = field . rel . to if to not in all_models : raise ValueError print " %s o2o to %s " % ( repr ( model ), repr ( to )) one_to_one . append (( model , to )) elif isinstance ( field , ForeignKey ): to = field . rel . to if to not in all_models : raise ValueError print " %s fk to %s " % ( repr ( model ), repr ( to )) foreignkeys . append (( model , to )) elif isinstance ( field , ManyToManyField ): to = field . rel . to if to not in all_models : raise ValueError print " %s m2m to %s " % ( repr ( model ), repr ( to )) many_to_many . append (( model , to )) return foreignkeys , one_to_one , many_to_many def class_name ( cls ): name = repr ( cls ) name = name [ name . index ( "'" ) + 1 :] name = name [: name . index ( "'" )] return quoted ( name ) # A lot of code below this line is copied from some stuff I wrote for my paper. # Should probably be cleaned up. import os , collections BG_COLOR = 'black' FG_COLOR = 'white' RED = '"#B30000"' GREEN = '"#008F00"' PURPLE = '"#24006B"' YELLOW = '"#B38F00"' GREY = '"#8F8F8F"' COLORS = [ YELLOW , GREEN , PURPLE , RED ] def quoted ( s ): return '" %s "' % str ( s ) def nodename ( state ): return quoted ( state [ 1 ]) if ( state [ 1 ] != '' ) else quoted ( 'start' ) def attrs_string ( attrs ): return '[ %s ]' % ', ' . join ([ " %s = %s " % ( k , v ) for k , v in attrs . iteritems ()]) def model_attrs_string ( model , extra_node_attrs , labels ): attrs = {} attrs [ 'color' ] = GREEN if 'django' in repr ( model ) else PURPLE if not labels : attrs [ 'label' ] = '""' attrs . update ( extra_node_attrs . get ( model , {})) return attrs_string ( attrs ) def edge_attrs_string ( c ): return attrs_string ({ 'label' : quoted ( c ), }) def fg_attrs_string ( extra = {}): attrs = { 'color' : FG_COLOR , 'fontcolor' : FG_COLOR } attrs . update ( extra ) return attrs_string ( attrs ) def relns_to_dot ( models , foreignkeys , one_to_one , many_to_many , extra_node_attrs = {}, ** kwargs ): edge_len = kwargs . pop ( 'edgelen' , 3 ) labels = kwargs . pop ( 'labels' , True ) result = [] result . append ( 'digraph django_model_relationships {' ) result . append ( ' graph [bgcolor= %s ];' % BG_COLOR ) result . append ( ' node %s ;' % fg_attrs_string ()) result . append ( ' edge %s ;' % fg_attrs_string ({ 'len' : 3 })) result . append ( ' rankdir=LR;' ) result . append ( ' size="30,20!";' ) for model in sorted ( models ): result . append ( ' %s %s ;' % ( class_name ( model ), model_attrs_string ( model , extra_node_attrs , labels ))) for source , dest in foreignkeys : edge_label = 'fk' result . append ( ' %s -> %s %s ;' % ( class_name ( source ), class_name ( dest ), edge_attrs_string ( edge_label ))) for source , dest in one_to_one : edge_label = 'o2o' result . append ( ' %s -> %s %s ;' % ( class_name ( source ), class_name ( dest ), edge_attrs_string ( edge_label ))) for source , dest in many_to_many : edge_label = 'm2m' result . append ( ' %s -> %s %s ;' % ( class_name ( source ), class_name ( dest ), edge_attrs_string ( edge_label ))) result . append ( '}' ) return '

' . join ( result ) def write_dot ( dot , filename , neato_options = {}, * args , ** kwargs ): program = kwargs . pop ( 'program' , 'dot' ) f = open ( filename , 'w' ) f . write ( dot . encode ( 'utf-8' )) f . close () neato_options_str = ' ' . join ( "-G %s = %s " % pair for pair in neato_options . iteritems ()) cmd = " %s %s %s -T png -o %s .png" % ( program , filename , neato_options_str , filename ) os . system ( cmd ) def main (): filename = '/tmp/model_graph.dot' models = get_all_models () relns = get_relns ( models ) dot = relns_to_dot ( models , * relns ) write_dot ( dot , filename ) if __name__ == '__main__' : main ()