erdantic is a simple tool for drawing entity relationship diagrams (ERDs) for Python data model classes

Overview

erdantic: Entity Relationship Diagrams

Docs Status PyPI conda-forge tests codecov

erdantic is a simple tool for drawing entity relationship diagrams (ERDs) for Python data model classes. Diagrams are rendered using the venerable Graphviz library. Supported data modeling frameworks are:

Features include a convenient CLI, automatic native rendering in Jupyter notebooks, and easy extensibility to other data modeling frameworks. Docstrings are even accessible as tooltips for SVG outputs. Great for adding a simple and clean data model reference to your documentation.

Example diagram created by erdantic

Installation

erdantic's graph modeling depends on pygraphviz and Graphviz, an open-source C library. If you are on Linux or macOS, the easiest way to install everything together is to use conda and conda-forge:

conda install erdantic -c conda-forge

If not using conda, Graphviz must be installed first (before you can install pygraphviz). For recommended options and installation troubleshooting, see the pygraphviz docs. Then to install erdantic and its Python dependencies from PyPI:

pip install erdantic

Development version

You can get the development version from GitHub with:

pip install https://github.com/drivendataorg/erdantic.git#egg=erdantic

Quick Usage

The fastest way to produce a diagram like the above example is to use the erdantic CLI. Simply specify the full dotted path to your data model class and an output path. The rendered format is interpreted from the filename extension.

erdantic erdantic.examples.pydantic.Party -o diagram.png

You can also import the erdantic Python library and use its functions.

import erdantic as erd
from erdantic.examples.pydantic import Party

# Easy one-liner
erd.draw(Party, out="diagram.png")

# Or create a diagram object that you can inspect and do stuff with
diagram = erd.create(Party)
diagram.models
#> [PydanticModel(Adventurer), PydanticModel(Party), PydanticModel(Quest), PydanticModel(QuestGiver)]
diagram.draw("diagram.png")

Check out the "Usage Examples" section of our docs to see more.

Comments
  • Fix issue when a model has a Literal

    Fix issue when a model has a Literal

    A Literal is special in that the argument to the literal is the actual collection of exact values that are permissible to that field.


    Bug description. Start with a file named erdbug.py with the following contents:

    from pydantic import BaseModel
    from typing import Literal
    
    class Species(BaseModel):
        name: str
    
    class PigmentedFlower(BaseModel):
        color: str
        species: Species
    
    class BlackFlower(PigmentedFlower):
        color: Literal["black"] = "black"
    
    class BlueFlower(PigmentedFlower):
        color: Literal["blue"] = "blue"
    

    If we run the following command:

    $ PYTHONPATH=`pwd` erdantic -o diagram.png erdbug.PigmentedFlower erdbug.BlackFlower erdbug.BlueFlower erdbug.Species
    

    We get the following error:

    Traceback (most recent call last):
      File "/Applications/Anaconda/anaconda3/envs/foundry-dev/bin/erdantic", line 33, in <module>
        sys.exit(load_entry_point('erdantic', 'console_scripts', 'erdantic')())
      File "/Applications/Anaconda/anaconda3/envs/foundry-dev/lib/python3.8/site-packages/typer/main.py", line 214, in __call__
        return get_command(self)(*args, **kwargs)
      File "/Applications/Anaconda/anaconda3/envs/foundry-dev/lib/python3.8/site-packages/click/core.py", line 1130, in __call__
        return self.main(*args, **kwargs)
      File "/Applications/Anaconda/anaconda3/envs/foundry-dev/lib/python3.8/site-packages/click/core.py", line 1055, in main
        rv = self.invoke(ctx)
      File "/Applications/Anaconda/anaconda3/envs/foundry-dev/lib/python3.8/site-packages/click/core.py", line 1404, in invoke
        return ctx.invoke(self.callback, **ctx.params)
      File "/Applications/Anaconda/anaconda3/envs/foundry-dev/lib/python3.8/site-packages/click/core.py", line 760, in invoke
        return __callback(*args, **kwargs)
      File "/Applications/Anaconda/anaconda3/envs/foundry-dev/lib/python3.8/site-packages/typer/main.py", line 500, in wrapper
        return callback(**use_params)  # type: ignore
      File "/Users/swails/src/entos/erdantic/erdantic/cli.py", line 87, in main
        diagram = create(*model_classes, termini=termini_classes)
      File "/Users/swails/src/entos/erdantic/erdantic/erd.py", line 192, in create
        search_composition_graph(model=model, seen_models=seen_models, seen_edges=seen_edges)
      File "/Users/swails/src/entos/erdantic/erdantic/erd.py", line 245, in search_composition_graph
        raise StringForwardRefError(
    erdantic.exceptions.StringForwardRefError: Forward reference 'black' for field 'color' on model 'BlackFlower' is a string literal and not a typing.ForwardRef object. erdantic is unable to handle forward references that aren't transformed into typing.ForwardRef. Declare explicitly with 'typing.ForwardRef("black", is_argument=False)'.
    

    The problem is that it's trying to evaluate the value passed to the Literal as a forward reference if it's a string, when in reality Literal is a special typing utility to indicate that the variable carries a specific value.

    After this PR, the following diagram is generated:

    diagram

    opened by swails 7
  • Support docstrings from pydantic.Field descriptions

    Support docstrings from pydantic.Field descriptions

    Adds support for showing field documentation from Pydantic models with descriptions set with Field(description=...) in SVG tooltips. This will add an "Attributes" section to the tooltip using Google-style docstring format and lists fields where the description keyword argument is used.

    Here's an example:

    from typing import Any, List, Optional
    
    import erdantic as erd
    from pydantic import BaseModel, Field
    
    class MyClassWithDescriptions(BaseModel):
        """This is the docstring for my class with descriptions."""
    
        hint_only: str
        has_descr_no_default: List[int] = Field(description="An array of numbers.")
        has_descr_ellipsis_default: List[int] = Field(..., description="Another array of numbers.")
        no_descr_has_default: Any = Field(10)
        has_descr_has_default: Optional[str] = Field(None, description="An optional string.")
    
    diagram = erd.create(MyClassWithDescriptions)
    
    print(diagram.models[0].docstring)
    #> __reprex__.MyClassWithDescriptions
    #> 
    #> This is the docstring for my class with descriptions.
    #> 
    #> Attributes:
    #>     has_descr_no_default (List[int]): An array of numbers.
    #>     has_descr_ellipsis_default (List[int]): Another array of numbers.
    #>     has_descr_has_default (Optional[str]): An optional string. Default is None.
    
    diagram.draw("myclasswithdescriptions.svg")
    #> None
    

    Created at 2021-11-06 11:17:05 EDT by reprexlite v0.4.3

    It doesn't seem like GitHub lets me upload an SVG. Here's a Google Drive link to the output: https://drive.google.com/uc?id=1hGhaq-pLcBP7EanA7uBos8QBuzreSEVv&export=download

    opened by jayqi 5
  • Handle forward references

    Handle forward references

    • Fixed handling of forward references in field type declarations. Evaluated forward references will be properly identified. Forward references not converted to typing.ForwardRef will throw a StringForwardRefError with instructions for how to resolve. Unevaluated forward references will throw an UnevaluatedForwardRefError with instructions for how to resolve.
    • Changed name of erdantic.errors module to erdantic.exceptions.
    • Added new ErdanticException base class from which other exceptions raised within the erdantic library are subclassed from. Changed several existing ValueError exceptions to new exception classes that subclass both ErdanticException and ValueError.
    • Changed __lt__ method on Model and Edge to return NotImplemented instead of raising an exception to follow typical convention for unsupported input types.

    Example

    import dataclasses
    import typing
    
    import erdantic as erd
    
    
    @dataclasses.dataclass
    class A:
        bees: typing.List['B']
    
    
    @dataclasses.dataclass
    class B:
        x: int
    
    # Unevaluated forward ref will error
    diagram = erd.create(A)
    #> Traceback (most recent call last):
    #>   File "<string>", line 2, in <module>
    #>   File "/Users/jqi/repos/erdantic/erdantic/erd.py", line 190, in create
    #>     search_composition_graph(model=model, seen_models=seen_models, seen_edges=seen_edges)
    #>   File "/Users/jqi/repos/erdantic/erdantic/erd.py", line 239, in search_composition_graph
    #>     raise UnevaluatedForwardRefError(
    #> erdantic.exceptions.UnevaluatedForwardRefError: Unevaluated forward reference 'B' for field bees on model A. Call 'typing.get_type_hints' on your dataclass after creating it to resolve.
    
    # Force evaluation with typing.get_type_hints and then it will be fine after that
    _ = typing.get_type_hints(A, localns=locals())
    diagram = erd.create(A)
    diagram.edges
    #> [ Edge(source=DataClassModel(A), source_field=<DataClassField: 'bees', List[B]>, target=DataClassModel(B))]
    diagram.draw("diagram.png")
    

    Created at 2021-10-28 00:48:39 EDT by reprexlite v0.4.2

    diagram

    Closes #40

    opened by jayqi 4
  • ERROR: Cannot unpack file pip-unpack-fpbmoqaz\erdantic.git (content-type: text/html; charset=utf-8); cannot detect archive format

    ERROR: Cannot unpack file pip-unpack-fpbmoqaz\erdantic.git (content-type: text/html; charset=utf-8); cannot detect archive format

    Microsoft Windows [Version 10.0.22000.1098]
    (c) Microsoft Corporation. All rights reserved.
    
    C:\Users\Max>pip install https://github.com/drivendataorg/erdantic.git#egg=erdantic
    Collecting erdantic
      Downloading https://github.com/drivendataorg/erdantic.git
         | 214.4 kB 29.0 kB/s 0:00:07
      ERROR: Cannot unpack file C:\Users\Max\AppData\Local\Temp\pip-unpack-fpbmoqaz\erdantic.git (downloaded from C:\Users\Max\AppData\Local\Temp\pip-install-6ohmio06\erdantic_d45a27d4d8f1499baaee813a654749bf, content-type: text/html; charset=utf-8); cannot detect archive format
    ERROR: Cannot determine archive format of C:\Users\Max\AppData\Local\Temp\pip-install-6ohmio06\erdantic_d45a27d4d8f1499baaee813a654749bf
    
    opened by BaseMax 3
  • Follow edges in a graph automatically

    Follow edges in a graph automatically

    test case:

    https://github.com/adsharma/fquery/blob/master/tests/mock_user.py

    Right now, I get only one class in the diagram, namely: MockUser. Would like to see "friends" and "reviews" edges/objects in the output.

    opened by adsharma 3
  • Show docstrings in rendered tables

    Show docstrings in rendered tables

    Sometimes data classes have docstrings, for the overall class or for individual attributes. It may be useful to be able to show those docstrings.

    How the visual design will work is an open question. The tables are currently compact and easy to read—it may require some creativity to add docstrings in a way that don't detract too much from that.

    enhancement help wanted 
    opened by jayqi 3
  • Hold back nbconvert until rendering SVG is fixed

    Hold back nbconvert until rendering SVG is fixed

    This is not an mkdocs-jupyter problem (https://github.com/danielfrg/mkdocs-jupyter/issues/92), instead it is a problem with nbconvert.

    Looks like there was a partial fix here: https://github.com/jupyter/nbconvert/pull/1837 And finishing that is tracked here: https://github.com/jupyter/nbconvert/issues/1849

    If repro'd this locally with 6.5, but the docs built successfully at 6.4 Pinning to a lower version for now and will update #68 to track removing this pin when the nbconvert issue is fixed.

    opened by pjbull 2
  • Clean up old Python 3.6 code paths

    Clean up old Python 3.6 code paths

    There are a few code paths for handling Python 3.6 typing library quirks. These should no longer be necessary.

    Also minor documentation changes:

    • Switches mkdocstrings handler back to python-legacy. The new python handler doesn't yet properly handle properties. https://github.com/mkdocstrings/python/issues/9
    • Adds TOC entries for modules so they show up in the Intersphinx inventory file
    opened by jayqi 2
  • Add python extra for mkdocstrings

    Add python extra for mkdocstrings

    mkdocstrings 0.19.0 requires that the python extra be explicitly installed.

    Also, to make tests pass with this version, we needed to do #51 as well and drop support for Python 3.6.

    Closes #55 Closes #51

    opened by pjbull 2
  • Support for Pandera

    Support for Pandera

    Pandera allows to create schema's and validations for Pandas dataframes. Is it possible that the erdantic tool supports the Pandera schema's? These can be exported to YAML, so perhaps that's an accessible way to read the schema's?

    opened by ba-tno 2
  • Add versioned docs

    Add versioned docs

    Adds versioned docs using mike.

    • Merges to main deploy as the dev version.
    • Releases deploy as <major>.<minor> version, overwriting previous versions that have the same major and minor. The default alias stable is updated to point at the new release.
    opened by jayqi 2
  • No module found error without setting PYTHONPATH

    No module found error without setting PYTHONPATH

    Logging this issue so that other people don't have to suffer as much as I did.

    I copied the same dataclass example into a newdataclass.py python file and ran erdantic newdataclass -o diagram.png and got the following error.

    After about 30 mins of debugging by running

    from importlib import import_module
    import_module("newdataclass")
    

    I figured it was because PYTHONPATH wasn't set to the current directory.

    Solutions

    1. Updating the documentation to mention this explicitly
    2. Better error message. Only on scrolling I found a whole traceback of exceptions. Importing module, importing class, etc.,

    Error Log

    Traceback (most recent call last):
    
      File "/Users/bhavaniravi/.virtualenvs/python-everyday/lib/python3.9/site-packages/erdantic/cli.py", line 131, in import_object_from_name
        return import_module(full_obj_name)
    
      File "/opt/homebrew/Cellar/[email protected]/3.9.6/Frameworks/Python.framework/Versions/3.9/lib/python3.9/importlib/__init__.py", line 127, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
    
      File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
    
      File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
    
      File "<frozen importlib._bootstrap>", line 984, in _find_and_load_unlocked
    
    ModuleNotFoundError: No module named 'newdataclass'
    
    
    During handling of the above exception, another exception occurred:
    
    
    Traceback (most recent call last):
    
      File "/Users/bhavaniravi/.virtualenvs/python-everyday/bin/erdantic", line 8, in <module>
        sys.exit(app())
    
      File "/Users/bhavaniravi/.virtualenvs/python-everyday/lib/python3.9/site-packages/erdantic/cli.py", line 108, in main
        model_or_module_objs = [import_object_from_name(mm) for mm in models_or_modules]
    
      File "/Users/bhavaniravi/.virtualenvs/python-everyday/lib/python3.9/site-packages/erdantic/cli.py", line 108, in <listcomp>
        model_or_module_objs = [import_object_from_name(mm) for mm in models_or_modules]
    
      File "/Users/bhavaniravi/.virtualenvs/python-everyday/lib/python3.9/site-packages/erdantic/cli.py", line 135, in import_object_from_name
        module_name, obj_name = full_obj_name.rsplit(".", 1)
    
    ValueError: not enough values to unpack (expected 2, got 1)
    
    opened by bhavaniravi 1
  • NBConvert does not handle SVG correctly causing docs build to fail

    NBConvert does not handle SVG correctly causing docs build to fail

    opened by github-actions[bot] 10
  • PlantUML generation?

    PlantUML generation?

    When I stumbled on this project, what I was really looking for was a way to generate an ERD in PlantUML. I've implemented that for personal use, but I'm happy to contribute the work back if it would be considered for inclusion. The changes are quite small.

    This was a remarkably easy code base to grok and dig into, so kudos on a useful project!

    opened by swails 1
  • Separating class and its inherited class

    Separating class and its inherited class

    As a user I want to see the relation between a class and its inherited class and also separate the members where it should be So that you will get a correct UML view of the classes.

    example: https://github.com/drivendataorg/erdantic/blob/main/erdantic/examples/pydantic.py

    Additional class:

    class PlannedParty(Party):
        """A planned group of adventurers finding themselves doing and saying things altogether unexpected.
        Attributes:
            name (str): Name that party is known by
            formed_datetime (datetime): Timestamp of when the party was formed
            members (List[Adventurer]): Adventurers that belong to this party
            active_quest (Optional[Quest]): Current quest that party is actively tackling
            planned_quests (List[Quest]): A list of quest that party which can be tackled
        """
    
        planned_quests: List[Quest] = []
    

    (Although this is an erd package, it can have added value. I understand if this issue is closed as it might not fit the vision.)

    opened by zamiramir 0
  • Write tests against static rendered png and svg outputs

    Write tests against static rendered png and svg outputs

    Right now we don't fully check rendered content on diagrams, but we should.

    ~We should do this in a parameterized way to reduce the amount of testing code to be maintained. We can use pytest's fixtures to load the example classes and/or created diagram objects.~ Addressed in #21.

    #21 addressed creating the static outputs, and has a test that checks the against the static DOT files. Still need tests that check png and svg formats.

    enhancement 
    opened by jayqi 0
Releases(v0.5.0)
  • v0.5.0(Jul 30, 2022)

    • Removed support for Python 3.6. (Issue #51, PR #56)
    • Added support for modules as inputs to all entrypoints to diagram creation (create, draw, to_dot, CLI). For all modules passed, erdantic will find all supported data model classes in each module. (Issue #23, PR #58)
      • Added new parameter limit_search_models_to to all entrypoints to allow for limiting which data model classes will be yielded from searching a module.
    Source code(tar.gz)
    Source code(zip)
  • v0.4.1(Apr 8, 2022)

  • v0.4.0(Nov 6, 2021)

    • Added support for showing field documentation from Pydantic models with descriptions set with Field(description=...) in SVG tooltips. This will add an "Attributes" section to the tooltip using Google-style docstring format and lists fields where the description keyword argument is used. (Issue #8, PR #42)
    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Oct 29, 2021)

    • Fixed handling of forward references in field type declarations. Evaluated forward references will be properly identified. Forward references not converted to typing.ForwardRef will throw a StringForwardRefError with instructions for how to resolve. Unevaluated forward references will throw an UnevaluatedForwardRefError with instructions for how to resolve. See new documentation for more details. (Issue #40, PR #41)
    • Changed name of erdantic.errors module to erdantic.exceptions. (PR #41)
    • Added new ErdanticException base class from which other exceptions raised within the erdantic library are subclassed from. Changed several existing ValueError exceptions to new exception classes that subclass both ErdanticException and ValueError. (PR #41)
    • Changed __lt__ method on Model and Edge to return NotImplemented instead of raising an exception to follow typical convention for unsupported input types. (PR #41)
    Source code(tar.gz)
    Source code(zip)
  • v0.2.1(Feb 17, 2021)

  • v0.2.0(Feb 15, 2021)

  • v0.1.2(Feb 11, 2021)

    • Fixed bug where Pydantic fields were missing generics in their type annotations. (#19)
    • Add tests against static rendered DOT output. Change adapter tests to use parameterized fixtures. (#21)
    Source code(tar.gz)
    Source code(zip)
  • v0.1.1(Feb 11, 2021)

  • v0.1.0(Feb 10, 2021)

Owner
DrivenData
Data science competitions for social good.
DrivenData
Python package that generates hardware pinout diagrams as SVG images

PinOut A Python package that generates hardware pinout diagrams as SVG images. The package is designed to be quite flexible and works well for general

null 336 Dec 20, 2022
Draw tree diagrams from indented text input

Draw tree diagrams This repository contains two very different scripts to produce hierarchical tree diagrams like this one: $ ./classtree.py collectio

Luciano Ramalho 8 Dec 14, 2022
A toolkit to generate MR sequence diagrams

mrsd: a toolkit to generate MR sequence diagrams mrsd is a Python toolkit to generate MR sequence diagrams, as shown below for the basic FLASH sequenc

Julien Lamy 3 Dec 25, 2021
Python module for drawing and rendering beautiful atoms and molecules using Blender.

Batoms is a Python package for editing and rendering atoms and molecules objects using blender. A Python interface that allows for automating workflows.

Xing Wang 1 Jul 6, 2022
By default, networkx has problems with drawing self-loops in graphs.

By default, networkx has problems with drawing self-loops in graphs. It makes it hard to draw a graph with self-loops or to make a nicely looking chord diagram. This repository provides some code to draw self-loops nicely

Vladimir Shitov 5 Jan 6, 2022
This is simply repo for line drawing rendering using freestyle in Blender.

blender_freestyle_line_drawing This is simply repo for line drawing rendering using freestyle in Blender. how to use blender2935 --background --python

MaxLin 3 Jul 2, 2022
This is Pygrr PolyArt, a program used for drawing custom Polygon models for your Pygrr project!

This is Pygrr PolyArt, a program used for drawing custom Polygon models for your Pygrr project!

Isaac 4 Dec 14, 2021
A guide for using Bootstrap 5 classes in Dash Bootstrap Components V1

dash-bootstrap-cheatsheet This handy interactive cheatsheet makes it easy to use the Bootstrap 5 classes with your Dash app made with the latest versi

null 10 Dec 22, 2022
Yata is a fast, simple and easy Data Visulaization tool, running on python dash

Yata is a fast, simple and easy Data Visulaization tool, running on python dash. The main goal of Yata is to provide a easy way for persons with little programming knowledge to visualize their data easily.

Cybercreek 3 Jun 28, 2021
Simple plotting for Python. Python wrapper for D3xter - render charts in the browser with simple Python syntax.

PyDexter Simple plotting for Python. Python wrapper for D3xter - render charts in the browser with simple Python syntax. Setup $ pip install PyDexter

D3xter 31 Mar 6, 2021
A simple agent-based model used to teach the basics of OOP in my lectures

Pydemic A simple agent-based model of a pandemic. This is used to teach basic principles of object-oriented programming to master students. It is not

Fabien Maussion 2 Jun 8, 2022
A simple python tool for explore your object detection dataset

A simple tool for explore your object detection dataset. The goal of this library is to provide simple and intuitive visualizations from your dataset and automatically find the best parameters for generating a specific grid of anchors that can fit you data characteristics

GRADIANT - Centro Tecnolóxico de Telecomunicacións de Galicia 142 Dec 25, 2022
Active Transport Analytics Model (ATAM) is a new strategic transport modelling and data visualization framework for Active Transport as well as emerging micro-mobility modes

{ATAM} Active Transport Analytics Model Active Transport Analytics Model (“ATAM”) is a new strategic transport modelling and data visualization framew

Peter Stephan 0 Jan 12, 2022
Pebble is a stat's visualization tool, this will provide a skeleton to develop a monitoring tool.

Pebble is a stat's visualization tool, this will provide a skeleton to develop a monitoring tool.

Aravind Kumar G 2 Nov 17, 2021
Simple spectra visualization tool for astronomers

SpecViewer A simple visualization tool for astronomers. Dependencies Python >= 3.7.4 PyQt5 >= 5.15.4 pyqtgraph == 0.10.0 numpy >= 1.19.4 How to use py

null 5 Oct 7, 2021
simple tool to paint axis x and y

simple tool to paint axis x and y

G705 1 Oct 21, 2021
Squidpy is a tool for the analysis and visualization of spatial molecular data.

Squidpy is a tool for the analysis and visualization of spatial molecular data. It builds on top of scanpy and anndata, from which it inherits modularity and scalability. It provides analysis tools that leverages the spatial coordinates of the data, as well as tissue images if available.

Theis Lab 251 Dec 19, 2022
Rubrix is a free and open-source tool for exploring and iterating on data for artificial intelligence projects.

Open-source tool for exploring, labeling, and monitoring data for AI projects

Recognai 1.5k Jan 7, 2023
A command line tool for visualizing CSV/spreadsheet-like data

PerfPlotter Read data from CSV files using pandas and generate interactive plots using bokeh, which can then be embedded into HTML pages and served by

Gino Mempin 0 Jun 25, 2022