- 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
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
I am not sure I know what it can do. Can you add links to screenshots?
#
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.