Skip to content

Commit 7d78efb

Browse files
committed
[52] add database settings and models
1 parent a09f5ad commit 7d78efb

17 files changed

+646
-0
lines changed

alembic.ini

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# A generic, single database configuration.
2+
3+
[alembic]
4+
# path to migration scripts
5+
script_location = studio/alembic
6+
7+
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
8+
# Uncomment the line below if you want the files to be prepended with date and time
9+
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
10+
# for all available tokens
11+
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
12+
13+
# sys.path path, will be prepended to sys.path if present.
14+
# defaults to the current working directory.
15+
prepend_sys_path = .
16+
17+
# timezone to use when rendering the date within the migration file
18+
# as well as the filename.
19+
# If specified, requires the python-dateutil library that can be
20+
# installed by adding `alembic[tz]` to the pip requirements
21+
# string value is passed to dateutil.tz.gettz()
22+
# leave blank for localtime
23+
# timezone =
24+
25+
# max length of characters to apply to the
26+
# "slug" field
27+
# truncate_slug_length = 40
28+
29+
# set to 'true' to run the environment during
30+
# the 'revision' command, regardless of autogenerate
31+
# revision_environment = false
32+
33+
# set to 'true' to allow .pyc and .pyo files without
34+
# a source .py file to be detected as revisions in the
35+
# versions/ directory
36+
# sourceless = false
37+
38+
# version location specification; This defaults
39+
# to studio/alembic/versions. When using multiple version
40+
# directories, initial revisions must be specified with --version-path.
41+
# The path separator used here should be the separator specified by "version_path_separator" below.
42+
# version_locations = %(here)s/bar:%(here)s/bat:studio/alembic/versions
43+
44+
# version path separator; As mentioned above, this is the character used to split
45+
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
46+
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
47+
# Valid values for version_path_separator are:
48+
#
49+
# version_path_separator = :
50+
# version_path_separator = ;
51+
# version_path_separator = space
52+
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
53+
54+
# the output encoding used when revision files
55+
# are written from script.py.mako
56+
# output_encoding = utf-8
57+
58+
59+
[post_write_hooks]
60+
# post_write_hooks defines scripts or Python functions that are run
61+
# on newly generated revision scripts. See the documentation for further
62+
# detail and examples
63+
64+
# format using "black" - use the console_scripts runner, against the "black" entrypoint
65+
# hooks = black
66+
# black.type = console_scripts
67+
# black.entrypoint = black
68+
# black.options = -l 79 REVISION_SCRIPT_FILENAME
69+
70+
# Logging configuration
71+
[loggers]
72+
keys = root,sqlalchemy,alembic
73+
74+
[handlers]
75+
keys = console
76+
77+
[formatters]
78+
keys = generic
79+
80+
[logger_root]
81+
level = WARN
82+
handlers = console
83+
qualname =
84+
85+
[logger_sqlalchemy]
86+
level = WARN
87+
handlers =
88+
qualname = sqlalchemy.engine
89+
90+
[logger_alembic]
91+
level = INFO
92+
handlers =
93+
qualname = alembic
94+
95+
[handler_console]
96+
class = StreamHandler
97+
args = (sys.stderr,)
98+
level = NOTSET
99+
formatter = generic
100+
101+
[formatter_generic]
102+
format = %(levelname)-5.5s [%(name)s] %(message)s
103+
datefmt = %H:%M:%S

docker-compose.dev.yml

+2
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,5 @@ services:
2323
ports:
2424
- "3000:3000"
2525
command: ash -c 'yarn install && yarn start'
26+
environment:
27+
TZ: Asia/Tokyo

pyproject.toml

+4
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ dependencies = [
4646
"pydantic[email]",
4747
"pydantic[dotenv]",
4848
"python-jose[cryptography]",
49+
"alembic==1.9.2",
50+
"sqlmodel==0.0.8",
51+
"pymysql==1.0.2",
52+
"fastapi_pagination==0.12.5",
4953
"plotly==5.15.0",
5054
"kaleido==0.2.1",
5155
]

studio/__main_unit__.py

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from fastapi import FastAPI, Request
66
from fastapi.staticfiles import StaticFiles
77
from fastapi.templating import Jinja2Templates
8+
from fastapi_pagination import add_pagination
89
from starlette.middleware.cors import CORSMiddleware
910

1011
from studio.app.common.routers import (
@@ -24,6 +25,8 @@
2425

2526
app = FastAPI(docs_url="/docs", openapi_url="/openapi")
2627

28+
add_pagination(app)
29+
2730
# common routers
2831
app.include_router(algolist.router)
2932
app.include_router(auth.router)

studio/alembic/env.py

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from logging.config import fileConfig
2+
3+
from alembic import context
4+
from dotenv import load_dotenv
5+
from sqlalchemy import engine_from_config, pool
6+
from sqlmodel import SQLModel
7+
8+
from studio.app.common.db.config import DATABASE_CONFIG
9+
from studio.app.common.models import * # noqa
10+
11+
load_dotenv()
12+
13+
# this is the Alembic Config object, which provides
14+
# access to the values within the .ini file in use.
15+
config = context.config
16+
config.set_main_option("sqlalchemy.url", DATABASE_CONFIG.DATABASE_URL)
17+
# Interpret the config file for Python logging.
18+
# This line sets up loggers basically.
19+
if config.config_file_name is not None:
20+
fileConfig(config.config_file_name)
21+
22+
# add your model's MetaData object here
23+
# for 'autogenerate' support
24+
# from myapp import mymodel
25+
# target_metadata = mymodel.Base.metadata
26+
target_metadata = SQLModel.metadata
27+
28+
# other values from the config, defined by the needs of env.py,
29+
# can be acquired:
30+
# my_important_option = config.get_main_option("my_important_option")
31+
# ... etc.
32+
33+
34+
def run_migrations_offline() -> None:
35+
"""Run migrations in 'offline' mode.
36+
37+
This configures the context with just a URL
38+
and not an Engine, though an Engine is acceptable
39+
here as well. By skipping the Engine creation
40+
we don't even need a DBAPI to be available.
41+
42+
Calls to context.execute() here emit the given string to the
43+
script output.
44+
45+
"""
46+
url = config.get_main_option("sqlalchemy.url")
47+
context.configure(
48+
url=url,
49+
target_metadata=target_metadata,
50+
literal_binds=True,
51+
dialect_opts={"paramstyle": "named"},
52+
)
53+
54+
with context.begin_transaction():
55+
context.run_migrations()
56+
57+
58+
def run_migrations_online() -> None:
59+
"""Run migrations in 'online' mode.
60+
61+
In this scenario we need to create an Engine
62+
and associate a connection with the context.
63+
64+
"""
65+
connectable = engine_from_config(
66+
config.get_section(config.config_ini_section),
67+
prefix="sqlalchemy.",
68+
poolclass=pool.NullPool,
69+
)
70+
71+
with connectable.connect() as connection:
72+
context.configure(connection=connection, target_metadata=target_metadata)
73+
74+
with context.begin_transaction():
75+
context.run_migrations()
76+
77+
78+
if context.is_offline_mode():
79+
run_migrations_offline()
80+
else:
81+
run_migrations_online()

studio/alembic/script.py.mako

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""${message}
2+
3+
Revision ID: ${up_revision}
4+
Revises: ${down_revision | comma,n}
5+
Create Date: ${create_date}
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
${imports if imports else ""}
11+
12+
# revision identifiers, used by Alembic.
13+
revision = ${repr(up_revision)}
14+
down_revision = ${repr(down_revision)}
15+
branch_labels = ${repr(branch_labels)}
16+
depends_on = ${repr(depends_on)}
17+
18+
19+
def upgrade() -> None:
20+
${upgrades if upgrades else "pass"}
21+
22+
23+
def downgrade() -> None:
24+
${downgrades if downgrades else "pass"}

studio/alembic/versions/.gitkeep

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
"""add common tables
2+
3+
Revision ID: a19721f73cfb
4+
Revises:
5+
Create Date: 2023-09-21 18:02:50.136222
6+
7+
"""
8+
import sqlalchemy as sa
9+
from alembic import op
10+
from sqlalchemy.dialects import mysql
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "a19721f73cfb"
14+
down_revision = None
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade() -> None:
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.create_table(
22+
"organization",
23+
sa.Column("id", mysql.BIGINT(unsigned=True), nullable=False),
24+
sa.Column("name", sa.String(length=100), nullable=False),
25+
sa.Column(
26+
"created_at",
27+
sa.DateTime(),
28+
server_default=sa.text("CURRENT_TIMESTAMP"),
29+
nullable=True,
30+
),
31+
sa.PrimaryKeyConstraint("id"),
32+
)
33+
op.create_table(
34+
"roles",
35+
sa.Column("id", mysql.BIGINT(unsigned=True), nullable=False),
36+
sa.Column("role", sa.String(length=100), nullable=False),
37+
sa.Column(
38+
"created_at",
39+
sa.DateTime(),
40+
server_default=sa.text("CURRENT_TIMESTAMP"),
41+
nullable=True,
42+
),
43+
sa.PrimaryKeyConstraint("id"),
44+
)
45+
op.create_table(
46+
"users",
47+
sa.Column("id", mysql.BIGINT(unsigned=True), nullable=False),
48+
sa.Column("organization_id", mysql.BIGINT(unsigned=True), nullable=False),
49+
sa.Column("uid", sa.String(length=100), nullable=False),
50+
sa.Column("name", sa.String(length=100), nullable=False),
51+
sa.Column("email", sa.String(length=255), nullable=False),
52+
sa.Column("attributes", sa.JSON(), nullable=True),
53+
sa.Column(
54+
"created_at",
55+
sa.DateTime(),
56+
server_default=sa.text("CURRENT_TIMESTAMP"),
57+
nullable=True,
58+
),
59+
sa.Column(
60+
"updated_at",
61+
sa.DateTime(),
62+
server_default=sa.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
63+
nullable=True,
64+
),
65+
sa.Column("active", sa.Boolean(), nullable=False),
66+
sa.ForeignKeyConstraint(
67+
["organization_id"],
68+
["organization.id"],
69+
),
70+
sa.PrimaryKeyConstraint("id"),
71+
sa.UniqueConstraint("uid", name="idx_uid"),
72+
)
73+
op.create_table(
74+
"user_roles",
75+
sa.Column("id", mysql.BIGINT(unsigned=True), nullable=False),
76+
sa.Column("user_id", mysql.BIGINT(unsigned=True), nullable=False),
77+
sa.Column("role_id", mysql.BIGINT(unsigned=True), nullable=False),
78+
sa.Column(
79+
"created_at",
80+
sa.DateTime(),
81+
server_default=sa.text("CURRENT_TIMESTAMP"),
82+
nullable=True,
83+
),
84+
sa.ForeignKeyConstraint(
85+
["role_id"],
86+
["roles.id"],
87+
),
88+
sa.ForeignKeyConstraint(
89+
["user_id"],
90+
["users.id"],
91+
),
92+
sa.PrimaryKeyConstraint("id"),
93+
sa.UniqueConstraint("user_id", "role_id", name="idx_user_id"),
94+
)
95+
op.create_table(
96+
"workspaces",
97+
sa.Column("id", mysql.BIGINT(unsigned=True), nullable=False),
98+
sa.Column("name", sa.String(length=100), nullable=False),
99+
sa.Column("user_id", mysql.BIGINT(unsigned=True), nullable=False),
100+
sa.Column(
101+
"created_at",
102+
sa.DateTime(),
103+
server_default=sa.text("CURRENT_TIMESTAMP"),
104+
nullable=True,
105+
),
106+
sa.Column(
107+
"updated_at",
108+
sa.DateTime(),
109+
server_default=sa.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
110+
nullable=True,
111+
),
112+
sa.Column("deleted", sa.Boolean(), nullable=False),
113+
sa.ForeignKeyConstraint(["user_id"], ["users.id"], name="user"),
114+
sa.PrimaryKeyConstraint("id"),
115+
)
116+
op.create_table(
117+
"workspaces_share_users",
118+
sa.Column("id", mysql.BIGINT(unsigned=True), nullable=False),
119+
sa.Column("workspace_id", mysql.BIGINT(unsigned=True), nullable=False),
120+
sa.Column("user_id", mysql.BIGINT(unsigned=True), nullable=False),
121+
sa.Column(
122+
"created_at",
123+
sa.DateTime(),
124+
server_default=sa.text("CURRENT_TIMESTAMP"),
125+
nullable=True,
126+
),
127+
sa.ForeignKeyConstraint(
128+
["user_id"],
129+
["users.id"],
130+
),
131+
sa.ForeignKeyConstraint(
132+
["workspace_id"],
133+
["workspaces.id"],
134+
),
135+
sa.PrimaryKeyConstraint("id"),
136+
sa.UniqueConstraint("workspace_id", "user_id", name="idx_workspace_id_user_id"),
137+
)
138+
# ### end Alembic commands ###
139+
140+
141+
def downgrade() -> None:
142+
# ### commands auto generated by Alembic - please adjust! ###
143+
op.drop_table("workspaces_share_users")
144+
op.drop_table("workspaces")
145+
op.drop_table("user_roles")
146+
op.drop_table("users")
147+
op.drop_table("roles")
148+
op.drop_table("organization")
149+
# ### end Alembic commands ###

0 commit comments

Comments
 (0)