Login

Simple Entity Relationship Diagram using GraphViz

Author:
bendavis78
Posted:
May 12, 2009
Language:
Python
Version:
1.0
Score:
1 (after 1 ratings)

This class generates a crows-foot notation ERD for your models (currently does not include fields) using GraphViz. The cardinality/modality of the relationships isn't perfect, but it works for 99% of cases out there.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import yapgvb
from django.db.models.fields.related import OneToOneRel, ManyToManyRel, ManyToOneRel

class Diagram:
    _graph = None
    _models = []
    _relationships = []
    _is_built = False

    def __init__(self, title='Django Model ERD'):
        self._graph = yapgvb.Digraph(title)

    def get_graph(self):
        return self._graph;

    def add_model(self, model):
        self._models.append(model)

        # get relationships
        for field in model._meta.fields:
            if field.rel:
                self.add_relationship(model, field)
        
        # m2m relationships
        for field in model._meta.local_many_to_many:
            self.add_relationship(model, field)

    def remove_model(self, model):
        try:
            del self._models[model]
        except IndexError:
            pass

    def add_relationship(self, model, field):
        self._relationships.append((model,field))

    def render(self, format='png', engine='dot'):
        self._build()
        self._graph.layout(engine)
        file = '/tmp/graph.%s' % format
        self._graph.render(file, format=format)
        content = open(file, 'r').read()
        import os; os.remove(file)
        return content

   
    def _build(self):
        if self._is_built:
            return
    
        nodes = {}
        for model in self._models:
            name = model.__name__
            label = model.__name__
            nodes[name] = self._graph.add_node(name, label=label, shape='record')
        
        for model, field in self._relationships:
            if nodes.get(field.rel.to.__name__):
                edge = self._graph.add_edge(nodes.get(model.__name__), nodes.get(field.rel.to.__name__))
                edge.arrowhead, edge.arrowtail = self._get_arrow(model, field)
                edge.minlen = 2


    def _get_arrow(self, model, field):
        map = {
            'many'     : 'crow',
            'one'      : 'tee',
            'required' : 'tee',
            'optional' : 'odot',
        }
        
        # get cardinality and modality
        if type(field.rel) == OneToOneRel:
            cardinality = ('one', 'one')
            
        elif type(field.rel) == ManyToOneRel:
            cardinality = ('one', 'many')

        elif type(field.rel) == ManyToManyRel:
            cardinality = ('many', 'many')
    
        
        # :KLUDGE: we're just guessing the most likely case for modality here
        if field.blank or type(field.rel) == ManyToManyRel:
            modality = ('optional', 'optional')
        else:
            modality = ('required', 'optional')

        return (map[cardinality[0]] + map[modality[0]],
                map[cardinality[1]] + map[modality[1]])


## USAGE ##
d = Diagram()
d.add_model(User, Group)
d.render()

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

whiskybar (on May 14, 2009):

I am not sure I know what it can do. Can you add links to screenshots?

#

bobo (on January 6, 2010):

This is pretty clever. The usage fails with two parameters, but the class works super sweet for simple ERD diagrams. Thanks for posting.

#

Please login first before commenting.