ErdDocs

Django models to ER diagram

Paste models.py and get the ER diagram of the tables Django creates — 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.

Your schema

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, both appear in every query you write against the database afterwards, and a diagram that leaves them out is a diagram of the code rather than of the schema.

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 is customer_id. You write the first and query the second.
  • A model with no key still gets one. Django adds an auto-incrementing id unless a field declares primary_key=True, and every foreign key in the project points at it.
  • Meta decides the name. db_table wins where you set it, and abstract = True means 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 — Django does more in Python than most ORMs, and these two stay there. The diagram leaves them out on purpose:

  • on_delete is 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. It is worth knowing before you delete rows with SQL rather than with the ORM.
  • 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's db_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 honest 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. The standard alternative, graph_models from django-extensions, is the right tool inside a project you own — and it wants the package installed, a working settings module, and a system Graphviz, which is a package manager away on a good day and a compilation problem on a bad one. The alternatives that skip Graphviz mostly send your models to a language model and ask it to draw the result, which 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. And once the schema is here it is not only a picture: the same models 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 — no package, no settings module, no system binary. graph_models from django-extensions is the standard answer and it is a good one; it needs the package installed in the project, a working settings module, and a system Graphviz — 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 — named after the model and the field, with an id and a foreign key to each side — and it is where half the confusion about a Django database comes from, because it appears in no models.py. It is drawn like any other table, because the database has it 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 rather than in models.py, which a pasted file cannot carry. That case is flagged once for the whole file rather than a prefix being invented — a name we cannot confirm is not a name we print.

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 — and the SQL export, which turns the same models into CREATE TABLE statements.

Related