| 1 |
#!/usr/bin/env python |
|---|
| 2 |
"""Django model to DOT (Graphviz) converter |
|---|
| 3 |
by Antonio Cavedoni <antonio@cavedoni.org> |
|---|
| 4 |
|
|---|
| 5 |
Make sure your DJANGO_SETTINGS_MODULE is set to your project and call |
|---|
| 6 |
the script like this: |
|---|
| 7 |
|
|---|
| 8 |
$ python modelviz.py <app_label> > <filename>.dot |
|---|
| 9 |
""" |
|---|
| 10 |
|
|---|
| 11 |
__version__ = "0.6" |
|---|
| 12 |
__svnid__ = "$Id$" |
|---|
| 13 |
__license__ = "Python" |
|---|
| 14 |
__author__ = "Antonio Cavedoni <http://cavedoni.com/>" |
|---|
| 15 |
__contributors__ = [ |
|---|
| 16 |
"Stefano J. Attardi <http://attardi.org/>", |
|---|
| 17 |
"limodou <http://www.donews.net/limodou/>", |
|---|
| 18 |
"Carlo C8E Miron" |
|---|
| 19 |
] |
|---|
| 20 |
|
|---|
| 21 |
from django.db import models |
|---|
| 22 |
from django.db.models import get_models |
|---|
| 23 |
from django.db.models.fields.related import \ |
|---|
| 24 |
ForeignKey, OneToOneField, ManyToManyField |
|---|
| 25 |
from django.contrib.contenttypes.generic import GenericRelation |
|---|
| 26 |
from django.template import Template, Context |
|---|
| 27 |
|
|---|
| 28 |
dot_template = """ |
|---|
| 29 |
digraph {{ name }} { |
|---|
| 30 |
fontname = "Helvetica" |
|---|
| 31 |
fontsize = 8 |
|---|
| 32 |
|
|---|
| 33 |
node [ |
|---|
| 34 |
fontname = "Helvetica" |
|---|
| 35 |
fontsize = 8 |
|---|
| 36 |
shape = "plaintext" |
|---|
| 37 |
] |
|---|
| 38 |
edge [ |
|---|
| 39 |
fontname = "Helvetica" |
|---|
| 40 |
fontsize = 8 |
|---|
| 41 |
] |
|---|
| 42 |
|
|---|
| 43 |
{% for model in models %} |
|---|
| 44 |
{{ model.name }} [label=< |
|---|
| 45 |
<TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0"> |
|---|
| 46 |
<TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4" |
|---|
| 47 |
><FONT FACE="Helvetica Bold" COLOR="white" |
|---|
| 48 |
>{{ model.name }}</FONT></TD></TR> |
|---|
| 49 |
{% for field in model.fields %} |
|---|
| 50 |
<TR><TD ALIGN="LEFT" BORDER="0" |
|---|
| 51 |
><FONT FACE="Helvetica Bold">{{ field.name }}</FONT |
|---|
| 52 |
></TD> |
|---|
| 53 |
<TD ALIGN="LEFT">{{ field.type }}</TD></TR> |
|---|
| 54 |
{% endfor %} |
|---|
| 55 |
</TABLE> |
|---|
| 56 |
>] |
|---|
| 57 |
|
|---|
| 58 |
{% for relation in model.relations %} |
|---|
| 59 |
{{ model.name }} -> {{ relation.target }} |
|---|
| 60 |
[label="{{ relation.type }}"] {{ relation.arrows }}; |
|---|
| 61 |
{% endfor %} |
|---|
| 62 |
{% endfor %} |
|---|
| 63 |
} |
|---|
| 64 |
""" |
|---|
| 65 |
|
|---|
| 66 |
def generate_dot(app_label): |
|---|
| 67 |
app = models.get_app(app_label) |
|---|
| 68 |
graph = Context({ |
|---|
| 69 |
'name': '"%s"' % app.__name__, |
|---|
| 70 |
'models': [] |
|---|
| 71 |
}) |
|---|
| 72 |
|
|---|
| 73 |
for appmodel in get_models(app): |
|---|
| 74 |
model = { |
|---|
| 75 |
'name': appmodel.__name__, |
|---|
| 76 |
'fields': [], |
|---|
| 77 |
'relations': [] |
|---|
| 78 |
} |
|---|
| 79 |
|
|---|
| 80 |
# model attributes |
|---|
| 81 |
def add_attributes(): |
|---|
| 82 |
model['fields'].append({ |
|---|
| 83 |
'name': field.name, |
|---|
| 84 |
'type': type(field).__name__ |
|---|
| 85 |
}) |
|---|
| 86 |
|
|---|
| 87 |
for field in appmodel._meta.fields: |
|---|
| 88 |
add_attributes() |
|---|
| 89 |
|
|---|
| 90 |
if appmodel._meta.many_to_many: |
|---|
| 91 |
for field in appmodel._meta.many_to_many: |
|---|
| 92 |
add_attributes() |
|---|
| 93 |
|
|---|
| 94 |
# relations |
|---|
| 95 |
def add_relation(extras=""): |
|---|
| 96 |
_rel = { |
|---|
| 97 |
'target': field.rel.to.__name__, |
|---|
| 98 |
'type': type(field).__name__, |
|---|
| 99 |
'arrows': extras |
|---|
| 100 |
} |
|---|
| 101 |
if _rel not in model['relations']: |
|---|
| 102 |
model['relations'].append(_rel) |
|---|
| 103 |
|
|---|
| 104 |
for field in appmodel._meta.fields: |
|---|
| 105 |
if isinstance(field, ForeignKey): |
|---|
| 106 |
add_relation() |
|---|
| 107 |
elif isinstance(field, OneToOneField): |
|---|
| 108 |
add_relation("[arrowhead=none arrowtail=none]") |
|---|
| 109 |
|
|---|
| 110 |
if appmodel._meta.many_to_many: |
|---|
| 111 |
for field in appmodel._meta.many_to_many: |
|---|
| 112 |
if isinstance(field, ManyToManyField): |
|---|
| 113 |
add_relation("[arrowhead=normal arrowtail=normal]") |
|---|
| 114 |
elif isinstance(field, GenericRelation): |
|---|
| 115 |
add_relation( |
|---|
| 116 |
'[style="dotted"] [arrowhead=normal arrowtail=normal]') |
|---|
| 117 |
graph['models'].append(model) |
|---|
| 118 |
|
|---|
| 119 |
t = Template(dot_template) |
|---|
| 120 |
return t.render(graph) |
|---|
| 121 |
|
|---|
| 122 |
if __name__ == "__main__": |
|---|
| 123 |
import sys |
|---|
| 124 |
try: |
|---|
| 125 |
app_label = sys.argv[1] |
|---|
| 126 |
print generate_dot(app_label) |
|---|
| 127 |
except IndexError: |
|---|
| 128 |
print __doc__ |
|---|