├── MANIFEST.in
├── requirements.txt
├── .github
└── workflows
│ └── python-publish.yml
├── LICENSE
├── setup.py
├── .gitignore
├── README.md
├── sqlalchemy_data_model_visualizer.py
└── my_interactive_data_model_diagram.svg
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include requirements.txt
2 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | sqlalchemy
2 | graphviz
3 | lxml
4 |
--------------------------------------------------------------------------------
/.github/workflows/python-publish.yml:
--------------------------------------------------------------------------------
1 | name: Build and Publish to PyPI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | release:
8 | types: [created]
9 |
10 | jobs:
11 | build-and-publish:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v3
15 |
16 | - name: Set up Python
17 | uses: actions/setup-python@v3
18 | with:
19 | python-version: '3.x'
20 |
21 | - name: Build wheel
22 | run: |
23 | python -m pip install --upgrade build
24 | python -m build
25 |
26 | - name: Publish to PyPI
27 | if: github.event_name == 'release' && github.event.action == 'created'
28 | run: |
29 | python -m pip install --upgrade twine
30 | twine upload dist/*
31 | env:
32 | TWINE_USERNAME: __token__
33 | TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Jeff Emanuel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 | from pathlib import Path
3 |
4 | # Define the directory where this setup.py file is located
5 | here = Path(__file__).parent
6 |
7 | # Read the contents of README file
8 | long_description = (here / 'README.md').read_text(encoding='utf-8')
9 |
10 | # Read the contents of requirements file
11 | requirements = (here / 'requirements.txt').read_text(encoding='utf-8').splitlines()
12 |
13 | setup(
14 | name='sqlalchemy_data_model_visualizer',
15 | version='0.1.3', # Update the version number for new releases
16 | description='A tool to visualize SQLAlchemy data models with Graphviz.',
17 | long_description=long_description,
18 | long_description_content_type='text/markdown',
19 | author='Jeffrey Emanuel',
20 | author_email='jeff@pastel.network',
21 | url='https://github.com/Dicklesworthstone/sqlalchemy_data_model_visualizer',
22 | py_modules=['sqlalchemy_data_model_visualizer'],
23 | install_requires=requirements,
24 | classifiers=[
25 | 'Development Status :: 3 - Alpha',
26 | 'Intended Audience :: Developers',
27 | 'License :: OSI Approved :: MIT License',
28 | 'Programming Language :: Python :: 3',
29 | 'Programming Language :: Python :: 3.8',
30 | 'Programming Language :: Python :: 3.9',
31 | 'Programming Language :: Python :: 3.10',
32 | 'Programming Language :: Python :: 3.11',
33 | ],
34 | license='MIT',
35 | keywords='sqlalchemy visualization graphviz data-model',
36 | include_package_data=True, # This tells setuptools to check MANIFEST.in for additional files
37 | )
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # poetry
98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102 | #poetry.lock
103 |
104 | # pdm
105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106 | #pdm.lock
107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108 | # in version control.
109 | # https://pdm.fming.dev/#use-with-ide
110 | .pdm.toml
111 |
112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113 | __pypackages__/
114 |
115 | # Celery stuff
116 | celerybeat-schedule
117 | celerybeat.pid
118 |
119 | # SageMath parsed files
120 | *.sage.py
121 |
122 | # Environments
123 | .env
124 | .venv
125 | env/
126 | venv/
127 | ENV/
128 | env.bak/
129 | venv.bak/
130 |
131 | # Spyder project settings
132 | .spyderproject
133 | .spyproject
134 |
135 | # Rope project settings
136 | .ropeproject
137 |
138 | # mkdocs documentation
139 | /site
140 |
141 | # mypy
142 | .mypy_cache/
143 | .dmypy.json
144 | dmypy.json
145 |
146 | # Pyre type checker
147 | .pyre/
148 |
149 | # pytype static type analyzer
150 | .pytype/
151 |
152 | # Cython debug symbols
153 | cython_debug/
154 |
155 | # PyCharm
156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158 | # and can be added to the global gitignore or merged into this file. For a more nuclear
159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160 | #.idea/
161 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SQLAlchemy Data Model Visualizer
2 |
3 | ## Overview
4 |
5 | This Python-based utility generates high-quality, readable visualizations of your SQLAlchemy ORM models with almost no effort. With a focus on clarity and detail, it uses Graphviz to render each model as a directed graph, making it easier to understand the relationships between tables in your database schema.
6 |
7 | 
8 |
9 | ## Try it Out Easily in Colab:
10 |
11 | [](https://colab.research.google.com/drive/1np5kPvDtdhq138eLHOGINYuTUMJo_wrj?usp=sharing)
12 |
13 | ## Features
14 |
15 | - Automatically maps SQLAlchemy ORM models to a directed graph.
16 | - Table-like representation of each model with fields, types, and constraints.
17 | - Export diagrams to SVG format for high-quality viewing and printing using Roboto font.
18 |
19 | ## Installation with pip and Usage:
20 |
21 | ```bash
22 | pip install sqlalchemy-data-model-visualizer
23 |
24 | # Suppose these are your SQLAlchemy data models defined above in the usual way, or imported from another file:
25 | models = [GenericUser, Customer, ContentCreator, UserSession, FileStorage, ServiceRequest, GenericAuditLog, GenericFeedback, GenericAPIKey, GenericNotification, GenericAPICreditLog, GenericSubscriptionType, GenericSubscription, GenericSubscriptionUsage, GenericBillingInfo]
26 | output_file_name = 'my_data_model_diagram'
27 | generate_data_model_diagram(models, output_file_name)
28 | add_web_font_and_interactivity('my_data_model_diagram.svg', 'my_interactive_data_model_diagram.svg')
29 | ```
30 |
31 | ## Installation from Source
32 |
33 | To get started, clone the repository and install the required packages.
34 |
35 | ```bash
36 | git clone https://github.com/Dicklesworthstone/sqlalchemy_data_model_visualizer.git
37 | cd sqlalchemy_data_model_visualizer
38 | python3 -m venv venv
39 | source venv/bin/activate
40 | python3 -m pip install --upgrade pip
41 | python3 -m pip install wheel
42 | pip install -r requirements.txt
43 | ```
44 |
45 | ## Requirements
46 |
47 | - Python 3.x
48 | - SQLAlchemy
49 | - Graphviz
50 | - lxml
51 |
52 | ## Usage
53 |
54 | ### Generate Data Model Diagram
55 |
56 | First, paste in your SQLAlchemy models. A set of fairly complex data models are provided in the code directly as an example-- just replace these with your own from your application.
57 |
58 | Then, simply call the `generate_data_model_diagram` function. This will generate an SVG file with the name `my_data_model_diagram.svg`.
59 |
60 | ## API Documentation
61 |
62 | ### `generate_data_model_diagram(models, output_file='my_data_model_diagram', add_labels=True)`
63 |
64 | - `models`: List of SQLAlchemy models you want to visualize.
65 | - `output_file`: Name of the output SVG file.
66 | - `add_labels`: Set to False to hide labels on the edges between tables
67 |
68 | ## Contributing
69 |
70 | Contributions are welcome! Please open an issue or submit a pull request.
71 |
72 | ## License
73 |
74 | This project is licensed under the MIT License.
75 |
76 | ---
77 |
78 | Thanks for your interest in my open-source project! I hope you find it useful. You might also find my commercial web apps useful, and I would really appreciate it if you checked them out:
79 |
80 | **[YoutubeTranscriptOptimizer.com](https://youtubetranscriptoptimizer.com)** makes it really quick and easy to paste in a YouTube video URL and have it automatically generate not just a really accurate direct transcription, but also a super polished and beautifully formatted written document that can be used independently of the video.
81 |
82 | The document basically sticks to the same material as discussed in the video, but it sounds much more like a real piece of writing and not just a transcript. It also lets you optionally generate quizzes based on the contents of the document, which can be either multiple choice or short-answer quizzes, and the multiple choice quizzes get turned into interactive HTML files that can be hosted and easily shared, where you can actually take the quiz and it will grade your answers and score the quiz for you.
83 |
84 | **[FixMyDocuments.com](https://fixmydocuments.com/)** lets you submit any kind of document— PDFs (including scanned PDFs that require OCR), MS Word and Powerpoint files, images, audio files (mp3, m4a, etc.) —and turn them into highly optimized versions in nice markdown formatting, from which HTML and PDF versions are automatically generated. Once converted, you can also edit them directly in the site using the built-in markdown editor, where it saves a running revision history and regenerates the PDF/HTML versions.
85 |
86 | In addition to just getting the optimized version of the document, you can also generate many other kinds of "derived documents" from the original: interactive multiple-choice quizzes that you can actually take and get graded on; slick looking presentation slides as PDF or HTML (using LaTeX and Reveal.js), an in-depth summary, a concept mind map (using Mermaid diagrams) and outline, custom lesson plans where you can select your target audience, a readability analysis and grade-level versions of your original document (good for simplifying concepts for students), Anki Flashcards that you can import directly into the Anki app or use on the site in a nice interface, and more.
87 |
--------------------------------------------------------------------------------
/sqlalchemy_data_model_visualizer.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from typing import Optional
3 | from enum import Enum
4 | from decimal import Decimal
5 | from sqlalchemy.orm import sessionmaker, declarative_base, relationship
6 | from sqlalchemy import Column, String, DateTime, Integer, Numeric, Boolean, JSON, ForeignKey, LargeBinary, Text, UniqueConstraint, CheckConstraint, text as sql_text
7 | from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
8 | from sqlalchemy import inspect
9 | import graphviz
10 | from lxml import etree
11 | import os
12 | import re
13 | Base = declarative_base()
14 |
15 | def generate_data_model_diagram(models, output_file='my_data_model_diagram', add_labels=True, view_diagram=True):
16 | # Initialize graph with more advanced visual settings
17 | dot = graphviz.Digraph(comment='Interactive Data Models', format='svg',
18 | graph_attr={'bgcolor': '#EEEEEE', 'rankdir': 'TB', 'splines': 'spline'},
19 | node_attr={'shape': 'none', 'fontsize': '12', 'fontname': 'Roboto'},
20 | edge_attr={'fontsize': '10', 'fontname': 'Roboto'})
21 |
22 | # Iterate through each SQLAlchemy model
23 | for model in models:
24 | insp = inspect(model)
25 | name = insp.class_.__name__
26 |
27 | # Create an HTML-like label for each model as a rich table
28 | label = f'''<
29 |
30 |
{name}
31 | '''
32 |
33 | for column in insp.columns:
34 | constraints = []
35 | if column.primary_key:
36 | constraints.append("PK")
37 | if column.unique:
38 | constraints.append("Unique")
39 | if column.index:
40 | constraints.append("Index")
41 |
42 | constraint_str = ','.join(constraints)
43 | color = "#BBDEFB"
44 |
45 | label += f'''