supabase-pydantic 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- supabase_pydantic-0.2.1/LICENSE +21 -0
- supabase_pydantic-0.2.1/PKG-INFO +54 -0
- supabase_pydantic-0.2.1/README.md +21 -0
- supabase_pydantic-0.2.1/pyproject.toml +211 -0
- supabase_pydantic-0.2.1/supabase_pydantic/cli.py +125 -0
- supabase_pydantic-0.2.1/supabase_pydantic/util/__init__.py +43 -0
- supabase_pydantic-0.2.1/supabase_pydantic/util/constants.py +128 -0
- supabase_pydantic-0.2.1/supabase_pydantic/util/dataclasses.py +458 -0
- supabase_pydantic-0.2.1/supabase_pydantic/util/db.py +29 -0
- supabase_pydantic-0.2.1/supabase_pydantic/util/fake.py +38 -0
- supabase_pydantic-0.2.1/supabase_pydantic/util/generator_helpers.py +233 -0
- supabase_pydantic-0.2.1/supabase_pydantic/util/json.py +14 -0
- supabase_pydantic-0.2.1/supabase_pydantic/util/marshalers.py +122 -0
- supabase_pydantic-0.2.1/supabase_pydantic/util/string.py +5 -0
- supabase_pydantic-0.2.1/supabase_pydantic/util/writer.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Kevin Boehm <kmbhm1@gmail.com>
|
|
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.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: supabase-pydantic
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: A project to translate Supabase schemas into Pydantic models
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: K Boehm
|
|
7
|
+
Author-email: kmbhm1@gmail.com
|
|
8
|
+
Requires-Python: >=3.10,<4.0
|
|
9
|
+
Classifier: Development Status :: 1 - Planning
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Framework :: FastAPI
|
|
12
|
+
Classifier: Framework :: Pydantic
|
|
13
|
+
Classifier: Framework :: Pydantic :: 2
|
|
14
|
+
Classifier: Framework :: Pytest
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Natural Language :: English
|
|
18
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
19
|
+
Classifier: Programming Language :: Python
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Requires-Dist: click (>=8.1.7,<9.0.0)
|
|
25
|
+
Requires-Dist: faker (>=24.11.0,<25.0.0)
|
|
26
|
+
Requires-Dist: isort (>=5.13.2,<6.0.0)
|
|
27
|
+
Requires-Dist: psycopg2-binary (>=2.9.9,<3.0.0)
|
|
28
|
+
Requires-Dist: pydantic (>=2.6.3,<3.0.0)
|
|
29
|
+
Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
|
|
30
|
+
Requires-Dist: types-psycopg2 (>=2.9.21.20240417,<3.0.0.0)
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# Supabase Pydantic Schemas
|
|
34
|
+
|
|
35
|
+
A project for digesting Supabase (or Postgres) table & view schemas into Pydantic models. Currently, this is ideal for projects that integrate [FastAPI](https://fastapi.tiangolo.com/) with [Supabase-Python](https://supabase.com/docs/reference/python/introduction), but more updates are coming soon ...
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
$ pip install supabase-pydantic # install
|
|
41
|
+
$ touch .env # create .env file
|
|
42
|
+
$ echo "DB_NAME=<your_db_name>" >> .env # add your postgres db name
|
|
43
|
+
$ echo "DB_USER=<your_db_user>" >> .env # add your postgres db user
|
|
44
|
+
$ echo "DB_PASS=<your_db_password>" >> .env # add your postgres db password
|
|
45
|
+
$ echo "DB_HOST=<your_db_host>" >> .env # add your postgres db host
|
|
46
|
+
$ echo "DB_PORT=<your_db_port>" >> .env # add your postgres db port
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
$ sb-pydantic # generate default schemas.py to ./entities/fastapi
|
|
53
|
+
```
|
|
54
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Supabase Pydantic Schemas
|
|
2
|
+
|
|
3
|
+
A project for digesting Supabase (or Postgres) table & view schemas into Pydantic models. Currently, this is ideal for projects that integrate [FastAPI](https://fastapi.tiangolo.com/) with [Supabase-Python](https://supabase.com/docs/reference/python/introduction), but more updates are coming soon ...
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
$ pip install supabase-pydantic # install
|
|
9
|
+
$ touch .env # create .env file
|
|
10
|
+
$ echo "DB_NAME=<your_db_name>" >> .env # add your postgres db name
|
|
11
|
+
$ echo "DB_USER=<your_db_user>" >> .env # add your postgres db user
|
|
12
|
+
$ echo "DB_PASS=<your_db_password>" >> .env # add your postgres db password
|
|
13
|
+
$ echo "DB_HOST=<your_db_host>" >> .env # add your postgres db host
|
|
14
|
+
$ echo "DB_PORT=<your_db_port>" >> .env # add your postgres db port
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
$ sb-pydantic # generate default schemas.py to ./entities/fastapi
|
|
21
|
+
```
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "supabase-pydantic"
|
|
3
|
+
version = "0.2.1"
|
|
4
|
+
description = "A project to translate Supabase schemas into Pydantic models"
|
|
5
|
+
authors = ["K Boehm <kmbhm1@gmail.com>"]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
classifiers = [
|
|
9
|
+
"Development Status :: 1 - Planning",
|
|
10
|
+
"Environment :: Console",
|
|
11
|
+
"Framework :: FastAPI",
|
|
12
|
+
"Framework :: Pydantic",
|
|
13
|
+
"Framework :: Pydantic :: 2",
|
|
14
|
+
"Framework :: Pytest",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Natural Language :: English",
|
|
18
|
+
"Operating System :: MacOS :: MacOS X",
|
|
19
|
+
"Programming Language :: Python",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[tool.poetry.scripts]
|
|
25
|
+
sb-pydantic = 'supabase_pydantic.cli:main'
|
|
26
|
+
supabase-pydantic = 'supabase_pydantic.cli:main'
|
|
27
|
+
|
|
28
|
+
[tool.poetry.dependencies]
|
|
29
|
+
python = "^3.10"
|
|
30
|
+
psycopg2-binary = "^2.9.9"
|
|
31
|
+
pydantic = "^2.6.3"
|
|
32
|
+
types-psycopg2 = "^2.9.21.20240417"
|
|
33
|
+
faker = "^24.11.0"
|
|
34
|
+
isort = "^5.13.2"
|
|
35
|
+
python-dotenv = "^1.0.1"
|
|
36
|
+
click = "^8.1.7"
|
|
37
|
+
|
|
38
|
+
[tool.poetry.group.dev.dependencies]
|
|
39
|
+
ruff = "^0.4.1"
|
|
40
|
+
sqlalchemy = {extras = ["mypy"], version = "^2.0.31"}
|
|
41
|
+
fastapi-jsonapi = "^2.8.0"
|
|
42
|
+
pytest = "^8.2.2"
|
|
43
|
+
python-semantic-release = "^9.8.5"
|
|
44
|
+
|
|
45
|
+
[tool.ruff]
|
|
46
|
+
line-length = 120
|
|
47
|
+
indent-width = 4
|
|
48
|
+
target-version = "py310"
|
|
49
|
+
|
|
50
|
+
[tool.ruff.lint]
|
|
51
|
+
select = [
|
|
52
|
+
"E", # pycodestyle
|
|
53
|
+
"F", # Pyflakes
|
|
54
|
+
"UP", # pyupgrade
|
|
55
|
+
"D", # pydocstyle
|
|
56
|
+
"I", # isort
|
|
57
|
+
"Q", # flake8-quotes
|
|
58
|
+
"W", # pycodestyle
|
|
59
|
+
]
|
|
60
|
+
exclude = [
|
|
61
|
+
".git",
|
|
62
|
+
".venv",
|
|
63
|
+
"__pycache__",
|
|
64
|
+
"build",
|
|
65
|
+
"dist",
|
|
66
|
+
"docs",
|
|
67
|
+
# "notebooks",
|
|
68
|
+
"**/*tests*",
|
|
69
|
+
"scripts",
|
|
70
|
+
"setup.py",
|
|
71
|
+
"setup.cfg",
|
|
72
|
+
"pyproject.toml",
|
|
73
|
+
"poetry.lock",
|
|
74
|
+
"pyrightconfig.json",
|
|
75
|
+
".*.yml",
|
|
76
|
+
".*.yaml",
|
|
77
|
+
".*.toml",
|
|
78
|
+
".*.md",
|
|
79
|
+
".*.txt",
|
|
80
|
+
".*.rst",
|
|
81
|
+
".*.json",
|
|
82
|
+
".*.html",
|
|
83
|
+
".*.css",
|
|
84
|
+
".*.js",
|
|
85
|
+
".*.png",
|
|
86
|
+
".*.jpg",
|
|
87
|
+
".*.jpeg",
|
|
88
|
+
".*.gif",
|
|
89
|
+
".*.svg",
|
|
90
|
+
".*.ico",
|
|
91
|
+
".*.eot",
|
|
92
|
+
".*.ttf",
|
|
93
|
+
".*.woff",
|
|
94
|
+
".*.woff2",
|
|
95
|
+
".*.map",
|
|
96
|
+
# ".*.ipynb",
|
|
97
|
+
".*.pyi",
|
|
98
|
+
".*.pyc",
|
|
99
|
+
".*.pyd",
|
|
100
|
+
".*.pyo",
|
|
101
|
+
".*.pyw",
|
|
102
|
+
".*.pyz",
|
|
103
|
+
".*.pyzw",
|
|
104
|
+
".*.DS_Store",
|
|
105
|
+
".*.gitignore",
|
|
106
|
+
".*.gitkeep",
|
|
107
|
+
".*.gitmodules",
|
|
108
|
+
".*.gitattributes",
|
|
109
|
+
".*.git",
|
|
110
|
+
".*.hgignore",
|
|
111
|
+
".*.hgkeep",
|
|
112
|
+
".*.",
|
|
113
|
+
]
|
|
114
|
+
ignore = [
|
|
115
|
+
"D100", # Missing docstring in public module
|
|
116
|
+
"D101", # Missing docstring in public class
|
|
117
|
+
"D104", # Missing docstring in public package
|
|
118
|
+
"D105", # Missing docstring in magic method
|
|
119
|
+
"D107", # Missing docstring in __init__
|
|
120
|
+
"D202", # No blank lines allowed after function docstring
|
|
121
|
+
"D206", # Docstring should be indented with spaces, not tabs
|
|
122
|
+
"E101", # indentation contains mixed spaces and tabs
|
|
123
|
+
"F403", # 'from module import *' used; unable to detect undefined names
|
|
124
|
+
"Q000", # Remove bad quotes
|
|
125
|
+
"Q002", # Remove bad quotes
|
|
126
|
+
"Q003", # Unnecessary single quotes
|
|
127
|
+
"W191", # Indentation contains tabs
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
[tool.ruff.lint.flake8-quotes]
|
|
131
|
+
docstring-quotes = "single"
|
|
132
|
+
|
|
133
|
+
[tool.ruff.lint.pydocstyle]
|
|
134
|
+
convention = "google"
|
|
135
|
+
|
|
136
|
+
[tool.ruff.format]
|
|
137
|
+
quote-style = "single"
|
|
138
|
+
indent-style = "space"
|
|
139
|
+
skip-magic-trailing-comma = true
|
|
140
|
+
line-ending = "auto"
|
|
141
|
+
|
|
142
|
+
[tool.mypy]
|
|
143
|
+
plugins = ["sqlalchemy.ext.mypy.plugin"]
|
|
144
|
+
|
|
145
|
+
[build-system]
|
|
146
|
+
requires = ["poetry-core"]
|
|
147
|
+
build-backend = "poetry.core.masonry.api"
|
|
148
|
+
|
|
149
|
+
[tool.semantic_release]
|
|
150
|
+
allow_zero_version = true
|
|
151
|
+
assets = []
|
|
152
|
+
branch = "main"
|
|
153
|
+
build_command = "poetry build"
|
|
154
|
+
build_command_env = []
|
|
155
|
+
changelog_file = "CHANGELOG.md"
|
|
156
|
+
commit_message = "{version}\n\nAutomatically generated by python-semantic-release"
|
|
157
|
+
commit_parser = "angular"
|
|
158
|
+
dist_path = "dist/"
|
|
159
|
+
logging_use_named_masks = false
|
|
160
|
+
major_on_zero = true
|
|
161
|
+
no_git_verify = false
|
|
162
|
+
patch_without_tag = true
|
|
163
|
+
remove_dist = false
|
|
164
|
+
tag_format = "v{version}"
|
|
165
|
+
upload_to_pypi = false
|
|
166
|
+
upload_to_release = true
|
|
167
|
+
version_toml = ["pyproject.toml:tool.poetry.version"]
|
|
168
|
+
|
|
169
|
+
[tool.semantic_release.branches.main]
|
|
170
|
+
match = "(main|master)"
|
|
171
|
+
prerelease_token = "rc"
|
|
172
|
+
prerelease = false
|
|
173
|
+
|
|
174
|
+
[tool.semantic_release.changelog]
|
|
175
|
+
changelog_file = "CHANGELOG.md"
|
|
176
|
+
exclude_commit_patterns = []
|
|
177
|
+
|
|
178
|
+
[tool.semantic_release.changelog.environment]
|
|
179
|
+
block_start_string = "{%"
|
|
180
|
+
block_end_string = "%}"
|
|
181
|
+
variable_start_string = "{{"
|
|
182
|
+
variable_end_string = "}}"
|
|
183
|
+
comment_start_string = "{#"
|
|
184
|
+
comment_end_string = "#}"
|
|
185
|
+
trim_blocks = false
|
|
186
|
+
lstrip_blocks = false
|
|
187
|
+
newline_sequence = "\n"
|
|
188
|
+
keep_trailing_newline = false
|
|
189
|
+
extensions = []
|
|
190
|
+
autoescape = true
|
|
191
|
+
|
|
192
|
+
[tool.semantic_release.commit_author]
|
|
193
|
+
env = "GIT_COMMIT_AUTHOR"
|
|
194
|
+
default = "KB <kmbhm1@gmail.com>"
|
|
195
|
+
|
|
196
|
+
[tool.semantic_release.commit_parser_options]
|
|
197
|
+
allowed_tags = ["build", "chore", "ci", "docs", "feat", "feature", "fix", "perf", "style", "refactor", "test"]
|
|
198
|
+
minor_tags = ["feat", "feature"]
|
|
199
|
+
patch_tags = ["fix", "perf"]
|
|
200
|
+
default_bump_level = 0
|
|
201
|
+
|
|
202
|
+
[tool.semantic_release.remote]
|
|
203
|
+
name = "origin"
|
|
204
|
+
type = "github"
|
|
205
|
+
ignore_token_for_push = false
|
|
206
|
+
insecure = false
|
|
207
|
+
|
|
208
|
+
[tool.semantic_release.publish]
|
|
209
|
+
dist_glob_patterns = ["dist/*"]
|
|
210
|
+
upload_to_vcs_release = true
|
|
211
|
+
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pprint
|
|
3
|
+
|
|
4
|
+
from dotenv import find_dotenv, load_dotenv
|
|
5
|
+
|
|
6
|
+
from supabase_pydantic.util import (
|
|
7
|
+
construct_table_info,
|
|
8
|
+
create_connection,
|
|
9
|
+
query_database,
|
|
10
|
+
check_connection,
|
|
11
|
+
GET_ALL_PUBLIC_TABLES_AND_COLUMNS,
|
|
12
|
+
GET_TABLE_COLUMN_DETAILS,
|
|
13
|
+
write_pydantic_model_string,
|
|
14
|
+
run_isort,
|
|
15
|
+
# write_sqlalchemy_model_string,
|
|
16
|
+
# write_jsonapi_pydantic_model_string,
|
|
17
|
+
)
|
|
18
|
+
from supabase_pydantic.util.constants import GET_CONSTRAINTS
|
|
19
|
+
import click
|
|
20
|
+
|
|
21
|
+
# Pretty print for testing
|
|
22
|
+
pp = pprint.PrettyPrinter(indent=4)
|
|
23
|
+
|
|
24
|
+
# Load environment variables from .env file
|
|
25
|
+
load_dotenv(find_dotenv())
|
|
26
|
+
|
|
27
|
+
# Replace these variables with your database connection details
|
|
28
|
+
db_name = os.environ.get('DB_NAME')
|
|
29
|
+
user = os.environ.get('DB_USER')
|
|
30
|
+
password = os.environ.get('DB_PASS')
|
|
31
|
+
host = os.environ.get('DB_HOST')
|
|
32
|
+
port = os.environ.get('DB_PORT')
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def reload_env() -> tuple:
|
|
36
|
+
# Load environment variables from .env file
|
|
37
|
+
load_dotenv(find_dotenv())
|
|
38
|
+
|
|
39
|
+
# Replace these variables with your database connection details
|
|
40
|
+
db_name = os.environ.get('DB_NAME')
|
|
41
|
+
user = os.environ.get('DB_USER')
|
|
42
|
+
password = os.environ.get('DB_PASS')
|
|
43
|
+
host = os.environ.get('DB_HOST')
|
|
44
|
+
port = os.environ.get('DB_PORT')
|
|
45
|
+
|
|
46
|
+
return db_name, user, password, host, port
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def check_readiness():
|
|
50
|
+
"""Check if environment variables are set correctly."""
|
|
51
|
+
# db_name, user, password, host, port = reload_env()
|
|
52
|
+
check = {'DB_NAME': db_name, 'DB_USER': user, 'DB_PASS': password, 'DB_HOST': host, 'DB_PORT': port}
|
|
53
|
+
for k, v in check.items():
|
|
54
|
+
# print(k, v)
|
|
55
|
+
if v is None:
|
|
56
|
+
print(f'Environment variables not set correctly. {k} is missing. Please set it in .env file.')
|
|
57
|
+
print('Exiting...')
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@click.command()
|
|
64
|
+
@click.option('--directory', default='entities', help='The directory .')
|
|
65
|
+
@click.option('--verbose', is_flag=True, help='Print verbose output.')
|
|
66
|
+
def main(directory: str, verbose: bool = False):
|
|
67
|
+
"""A CLI tool to generate Pydantic models from a PostgreSQL database."""
|
|
68
|
+
|
|
69
|
+
# Load environment variables from .env file & check if they are set correctly
|
|
70
|
+
load_dotenv(find_dotenv())
|
|
71
|
+
assert check_readiness()
|
|
72
|
+
|
|
73
|
+
default_directory = directory
|
|
74
|
+
# jsonapi_directory = os.path.join(default_directory, 'fastapi_jsonapi')
|
|
75
|
+
fastapi_directory = os.path.join(default_directory, 'fastapi')
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
# Create a connection to the database & check if connection is successful
|
|
79
|
+
conn = create_connection(db_name, user, password, host, port)
|
|
80
|
+
assert check_connection(conn)
|
|
81
|
+
|
|
82
|
+
# Fetch table column details & foreign key details
|
|
83
|
+
column_details = query_database(conn, GET_ALL_PUBLIC_TABLES_AND_COLUMNS)
|
|
84
|
+
fk_details = query_database(conn, GET_TABLE_COLUMN_DETAILS)
|
|
85
|
+
constraints = query_database(conn, GET_CONSTRAINTS)
|
|
86
|
+
tables = construct_table_info(column_details, fk_details, constraints)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
raise e
|
|
89
|
+
finally:
|
|
90
|
+
if conn:
|
|
91
|
+
conn.close()
|
|
92
|
+
print('Connection closed.')
|
|
93
|
+
|
|
94
|
+
# Create Pydantic models
|
|
95
|
+
pydantic_models_string = write_pydantic_model_string(tables)
|
|
96
|
+
pydantic_schemas_path = os.path.join(fastapi_directory, 'schemas.py')
|
|
97
|
+
|
|
98
|
+
# sql_alchemy_models_string = write_sqlalchemy_model_string(tables)
|
|
99
|
+
# sql_alchemy_models_path = os.path.join(jsonapi_directory, 'database.py')
|
|
100
|
+
|
|
101
|
+
# pydantic_jsonapi_models_string = write_jsonapi_pydantic_model_string(tables)
|
|
102
|
+
# pydantic_jsonapi_schemas_path = os.path.join(jsonapi_directory, 'schemas.py')
|
|
103
|
+
|
|
104
|
+
content = [pydantic_models_string] # , sql_alchemy_models_string, pydantic_jsonapi_models_string]
|
|
105
|
+
path = [pydantic_schemas_path] # , sql_alchemy_models_path, pydantic_jsonapi_schemas_path]
|
|
106
|
+
|
|
107
|
+
# Check if the directory exists, if not, create it
|
|
108
|
+
for d in [default_directory, fastapi_directory]: # , jsonapi_directory]:
|
|
109
|
+
if not os.path.exists(d):
|
|
110
|
+
os.makedirs(d)
|
|
111
|
+
|
|
112
|
+
# Define the full path to the file
|
|
113
|
+
for s, fp in zip(content, path):
|
|
114
|
+
with open(fp, 'w') as file:
|
|
115
|
+
file.write(s)
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
run_isort(fp)
|
|
119
|
+
except Exception as e:
|
|
120
|
+
print('An error occurred while running isort.')
|
|
121
|
+
print(e)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
if __name__ == '__main__':
|
|
125
|
+
main()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from .constants import (
|
|
2
|
+
RelationType,
|
|
3
|
+
ModelGenerationType,
|
|
4
|
+
GET_ALL_PUBLIC_TABLES_AND_COLUMNS,
|
|
5
|
+
GET_TABLE_COLUMN_DETAILS,
|
|
6
|
+
PYDANTIC_TYPE_MAP,
|
|
7
|
+
)
|
|
8
|
+
from .json import CustomJsonEncoder
|
|
9
|
+
from .fake import generate_fake_data
|
|
10
|
+
from .dataclasses import AsDictParent, ColumnInfo, ForeignKeyInfo, TableInfo
|
|
11
|
+
from .db import create_connection, query_database, check_connection
|
|
12
|
+
from .marshalers import construct_table_info
|
|
13
|
+
from .string import to_pascal_case
|
|
14
|
+
from .generator_helpers import (
|
|
15
|
+
write_pydantic_model_string,
|
|
16
|
+
run_isort,
|
|
17
|
+
write_sqlalchemy_model_string,
|
|
18
|
+
write_jsonapi_pydantic_model_string,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
'to_pascal_case',
|
|
23
|
+
'create_connection',
|
|
24
|
+
'check_connection',
|
|
25
|
+
'CustomJsonEncoder',
|
|
26
|
+
'generate_fake_data',
|
|
27
|
+
'AsDictParent',
|
|
28
|
+
'ColumnInfo',
|
|
29
|
+
'ForeignKeyInfo',
|
|
30
|
+
'RelationType',
|
|
31
|
+
'Model',
|
|
32
|
+
'ModelGenerationType',
|
|
33
|
+
'GET_ALL_PUBLIC_TABLES_AND_COLUMNS',
|
|
34
|
+
'GET_TABLE_COLUMN_DETAILS',
|
|
35
|
+
'TableInfo',
|
|
36
|
+
'PYDANTIC_TYPE_MAP',
|
|
37
|
+
'construct_table_info',
|
|
38
|
+
'query_database',
|
|
39
|
+
'write_pydantic_model_string',
|
|
40
|
+
'run_isort',
|
|
41
|
+
'write_sqlalchemy_model_string',
|
|
42
|
+
'write_jsonapi_pydantic_model_string',
|
|
43
|
+
]
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RelationType(str, Enum):
|
|
5
|
+
ONE_TO_ONE = 'One-to-One'
|
|
6
|
+
ONE_TO_MANY = 'One-to-Many'
|
|
7
|
+
MANY_TO_MANY = 'Many-to-Many'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ModelGenerationType(str, Enum):
|
|
11
|
+
PARENT = 'PARENT'
|
|
12
|
+
MAIN = 'MAIN'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
CUSTOM_MODEL_NAME = 'CustomModel'
|
|
16
|
+
CUSTOM_JSONAPI_META_MODEL_NAME = 'PydanticBaseModel'
|
|
17
|
+
BASE_CLASS_POSTFIX = 'BaseSchema'
|
|
18
|
+
|
|
19
|
+
CONSTRAINT_TYPE_MAP = {'p': 'PRIMARY KEY', 'f': 'FOREIGN KEY', 'u': 'UNIQUE', 'c': 'CHECK', 'x': 'EXCLUDE'}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
PYDANTIC_TYPE_MAP: dict[str, tuple[str, str | None]] = {
|
|
23
|
+
'integer': ('int', None),
|
|
24
|
+
'bigint': ('int', None),
|
|
25
|
+
'smallint': ('int', None),
|
|
26
|
+
'numeric': ('float', None),
|
|
27
|
+
'text': ('str', None),
|
|
28
|
+
'varchar': ('str', None),
|
|
29
|
+
'character varying': ('str', None),
|
|
30
|
+
'boolean': ('bool', None),
|
|
31
|
+
'timestamp': ('datetime', 'from datetime import datetime'),
|
|
32
|
+
'timestamp with time zone': ('datetime', 'from datetime import datetime'),
|
|
33
|
+
'timestamp without time zone': ('datetime', 'from datetime import datetime'),
|
|
34
|
+
'date': ('datetime.date', 'from datetime import date'),
|
|
35
|
+
'uuid': ('UUID4', 'from pydantic import UUID4'),
|
|
36
|
+
'json': ('dict | Json', 'from pydantic import Json'),
|
|
37
|
+
'jsonb': ('dict | Json', 'from pydantic import Json'),
|
|
38
|
+
'ARRAY': ('list', None),
|
|
39
|
+
# Add more data types and their imports as necessary.
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
SQLALCHEMY_TYPE_MAP: dict[str, tuple[str, str | None]] = {
|
|
44
|
+
'integer': ('Integer', 'from sqlalchemy import Integer'),
|
|
45
|
+
'bigint': ('BigInteger', 'from sqlalchemy import BigInteger'),
|
|
46
|
+
'smallint': ('SmallInteger', 'from sqlalchemy import SmallInteger'),
|
|
47
|
+
'numeric': ('Numeric', 'from sqlalchemy import Numeric'),
|
|
48
|
+
'text': ('Text', 'from sqlalchemy import Text'),
|
|
49
|
+
'varchar': ('String', 'from sqlalchemy import String'),
|
|
50
|
+
'character varying': ('String', 'from sqlalchemy import String'),
|
|
51
|
+
'boolean': ('Boolean', 'from sqlalchemy import Boolean'),
|
|
52
|
+
'timestamp': ('DateTime', 'from sqlalchemy import DateTime'),
|
|
53
|
+
'timestamp with time zone': ('DateTime', 'from sqlalchemy.dialects.postgresql import TIMESTAMP'),
|
|
54
|
+
'timestamp without time zone': ('DateTime', 'from sqlalchemy import DateTime'),
|
|
55
|
+
'date': ('Date', 'from sqlalchemy import Date'),
|
|
56
|
+
'uuid': ('UUID', 'from sqlalchemy.dialects.postgresql import UUID'),
|
|
57
|
+
'json': ('JSON', 'from sqlalchemy import JSON'),
|
|
58
|
+
'jsonb': ('JSONB', 'from sqlalchemy.dialects.postgresql import JSONB'),
|
|
59
|
+
'ARRAY': ('ARRAY', 'from sqlalchemy.dialects.postgresql import ARRAY'),
|
|
60
|
+
# Add more data types and their imports as necessary.
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# Queries
|
|
65
|
+
|
|
66
|
+
GET_TABLE_COLUMN_DETAILS = """
|
|
67
|
+
SELECT
|
|
68
|
+
tc.table_schema,
|
|
69
|
+
tc.table_name,
|
|
70
|
+
kcu.column_name,
|
|
71
|
+
ccu.table_schema AS foreign_table_schema,
|
|
72
|
+
ccu.table_name AS foreign_table_name,
|
|
73
|
+
ccu.column_name AS foreign_column_name,
|
|
74
|
+
tc.constraint_name
|
|
75
|
+
FROM
|
|
76
|
+
information_schema.table_constraints AS tc
|
|
77
|
+
JOIN
|
|
78
|
+
information_schema.key_column_usage AS kcu
|
|
79
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
80
|
+
AND tc.table_schema = kcu.table_schema
|
|
81
|
+
JOIN
|
|
82
|
+
information_schema.constraint_column_usage AS ccu
|
|
83
|
+
ON ccu.constraint_name = tc.constraint_name
|
|
84
|
+
AND ccu.table_schema = tc.table_schema
|
|
85
|
+
WHERE
|
|
86
|
+
tc.constraint_type = 'FOREIGN KEY'
|
|
87
|
+
AND tc.table_schema = 'public';
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
GET_ALL_PUBLIC_TABLES_AND_COLUMNS = """
|
|
91
|
+
SELECT
|
|
92
|
+
c.table_schema,
|
|
93
|
+
c.table_name,
|
|
94
|
+
c.column_name,
|
|
95
|
+
c.column_default,
|
|
96
|
+
c.is_nullable,
|
|
97
|
+
c.data_type,
|
|
98
|
+
c.character_maximum_length,
|
|
99
|
+
t.table_type
|
|
100
|
+
FROM
|
|
101
|
+
information_schema.columns AS c
|
|
102
|
+
JOIN
|
|
103
|
+
information_schema.tables AS t ON c.table_name = t.table_name AND c.table_schema = t.table_schema
|
|
104
|
+
WHERE
|
|
105
|
+
c.table_schema = 'public'
|
|
106
|
+
AND (t.table_type = 'BASE TABLE' OR t.table_type = 'VIEW')
|
|
107
|
+
ORDER BY
|
|
108
|
+
c.table_schema, c.table_name, c.ordinal_position;
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
GET_CONSTRAINTS = """
|
|
112
|
+
SELECT
|
|
113
|
+
conname AS constraint_name,
|
|
114
|
+
conrelid::regclass AS table_name,
|
|
115
|
+
array_agg(a.attname) AS columns,
|
|
116
|
+
contype AS constraint_type,
|
|
117
|
+
pg_get_constraintdef(oid) AS constraint_definition
|
|
118
|
+
FROM
|
|
119
|
+
pg_constraint
|
|
120
|
+
JOIN
|
|
121
|
+
pg_attribute AS a ON a.attnum = ANY (conkey) AND a.attrelid = conrelid
|
|
122
|
+
WHERE
|
|
123
|
+
connamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public')
|
|
124
|
+
GROUP BY
|
|
125
|
+
conname, conrelid, contype, oid
|
|
126
|
+
ORDER BY
|
|
127
|
+
conrelid::regclass::text, contype DESC;
|
|
128
|
+
"""
|