Django models to ER diagram
Paste models.py and get the ER diagram of the tables Django creates. That includes the _id columns behind your foreign keys, the primary key it adds for you, and the join tables a ManyToManyField makes. No Graphviz, no settings module, nothing uploaded.
Six models above, eight tables below. Paste your own file over the example.
The two tables nobody wrote
teams = models.ManyToManyField(Team) is one line in a model and one whole table in the database: support_agent_teams, with an id and a foreign key to each side. The same goes for the labels on a ticket. Neither appears in any models.py, yet both appear in every query you write against the database afterwards.
Django rewrites three other things on the way in, and the diagram above shows the results:
- A foreign key field is an _id column. The field is
customer; the column iscustomer_id. You write the first and query the second. - A model with no key still gets one. Django adds an auto-incrementing
idunless a field declaresprimary_key=True, and every foreign key in the project points at it. - Meta decides the name.
db_tablewins where you set it, andabstract = Truemeans no table at all. An abstract base is inheritance, not a schema.
Two things that look like schema
ErdDocs draws the database Django creates, and two of Django’s most familiar arguments never reach it. The diagram leaves them out on purpose:
on_deleteis not ON DELETE. Django enforces deletion in application code and emits a plain reference constraint. A diagram claiming the database cascades would be describing a rule the database does not have. Keep it in mind before you delete rows with plain SQL.default=is not a column default. It is applied when Python builds the row, so a row inserted by anything else gets nothing. Django 5'sdb_default=is the real one, and that is the one read here.
Where Meta.db_table is set, as it is in the example, ErdDocs uses the exact table name. The one gap is where it is not. Django names tables app_label_model, and a pasted file does not say which app it came from. So the model name is used, and ErdDocs flags that once for the whole file instead of inventing a prefix.
Why the usual route costs an afternoon
ErdDocs reads models.py as text, so the whole route is a paste into a browser tab. graph_models from django-extensions wants the package installed, a working settings module, and a system Graphviz. On a good day Graphviz is a package manager away; on a bad one it is a compilation problem. The alternatives that skip Graphviz mostly send your models to a language model and ask it to draw the result. That is a different trade again. Your code leaves the machine, and the answer is an approximation rather than a reading.
Neither cost applies to a paste. ErdDocs reads the file in the tab, and nothing is uploaded. The same models also produce a printable data dictionary, Mermaid or PlantUML for a README, and CREATE TABLE statements if what you actually needed was the SQL.
FAQ
Do I need django-extensions and Graphviz?
No. ErdDocs reads models.py as text you paste into a browser tab. There is no package, no settings module and no system binary. graph_models from django-extensions needs the package installed in the project, a working settings module, and a system Graphviz. Graphviz is the part that reliably costs an afternoon on a locked-down laptop or a fresh macOS.
Does it run my project?
ErdDocs never executes your code or imports Django. It reads the models as text, so a file with a missing import, a half-finished refactor, or a snippet copied out of a pull request still draws. There is no settings module to point at, no DJANGO_SETTINGS_MODULE, and no database connection.
Is my code uploaded anywhere?
No. The parser is JavaScript running in your browser, so the file is read on your own machine. This is the difference from the AI converters that answer the same question: they send your models to a server and approximate the result from a language model. ErdDocs is a parser, and the schema never leaves the tab.
What does it do with ManyToManyField?
It draws the table Django creates. A ManyToManyField with no through model makes a real table. The table is named after the model and the field, and it has an id and a foreign key to each side. Half the confusion about a Django database comes from this table, because it appears in no models.py. It is drawn like any other table.
Why is my table called post and not blog_post?
ErdDocs uses Meta.db_table exactly where you set it, so where the file states the name, the name on the diagram is exact. Where it does not, the model name is used. Django builds table names as app_label + "_" + model name, and the app label lives in the project layout, not in models.py. A pasted file cannot carry it. ErdDocs flags that case once for the whole file and does not invent a prefix.
Is it free?
Drawing and exporting the picture are free, no account, for schemas up to 25 tables. $9 once lifts the limit and unlocks the data dictionary. It also unlocks the SQL export, which turns the same models into CREATE TABLE statements.
Related
- SQLAlchemy models to ER diagram: the other Python ORM, same no-install argument.
- Sequelize models to ER diagram: the JavaScript side of the same job.
- Prisma schema to ER diagram: schema-first, with its own implicit join tables.
- Data dictionary generator: the models as a printable document, help_text included.
- Mermaid ER diagram generator: the same models as README text.
- SQL to ER diagram: the tool with every format it reads.