Skip to content

Commit a881d47

Browse files
committed
feat: sync tables and fields
1 parent f4402ab commit a881d47

File tree

8 files changed

+191
-21
lines changed

8 files changed

+191
-21
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""005_table_and_field
2+
3+
Revision ID: 0a6f11be9be4
4+
Revises: 8fe654655905
5+
Create Date: 2025-05-15 10:20:25.686576
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
import sqlmodel.sql.sqltypes
11+
12+
13+
# revision identifiers, used by Alembic.
14+
revision = '0a6f11be9be4'
15+
down_revision = '8fe654655905'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# ### commands auto generated by Alembic - please adjust! ###
22+
op.create_table('core_field',
23+
sa.Column('id', sa.Integer(), sa.Identity(always=True), nullable=False),
24+
sa.Column('ds_id', sa.BigInteger(), nullable=True),
25+
sa.Column('table_id', sa.BigInteger(), nullable=True),
26+
sa.Column('checked', sa.Boolean(), nullable=False),
27+
sa.Column('field_name', sa.Text(), nullable=True),
28+
sa.Column('field_type', sqlmodel.sql.sqltypes.AutoString(length=128), nullable=True),
29+
sa.Column('field_comment', sa.Text(), nullable=True),
30+
sa.Column('custom_comment', sa.Text(), nullable=True),
31+
sa.PrimaryKeyConstraint('id')
32+
)
33+
op.create_table('core_table',
34+
sa.Column('id', sa.Integer(), sa.Identity(always=True), nullable=False),
35+
sa.Column('ds_id', sa.BigInteger(), nullable=True),
36+
sa.Column('checked', sa.Boolean(), nullable=False),
37+
sa.Column('table_name', sa.Text(), nullable=True),
38+
sa.Column('table_comment', sa.Text(), nullable=True),
39+
sa.Column('custom_comment', sa.Text(), nullable=True),
40+
sa.PrimaryKeyConstraint('id')
41+
)
42+
# ### end Alembic commands ###
43+
44+
45+
def downgrade():
46+
# ### commands auto generated by Alembic - please adjust! ###
47+
op.drop_table('core_table')
48+
op.drop_table('core_field')
49+
# ### end Alembic commands ###

backend/apps/datasource/api/datasource.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from fastapi import APIRouter
22
from ..crud.datasource import get_datasource_list, check_status, create_ds, update_ds, delete_ds, getTables, getFields, execSql
33
from common.core.deps import SessionDep
4-
from ..models.datasource import CoreDatasource
4+
from ..models.datasource import CoreDatasource, CreateDatasource
55

66
router = APIRouter(tags=["datasource"], prefix="/datasource")
77

@@ -17,7 +17,7 @@ async def check(session: SessionDep, ds: CoreDatasource):
1717

1818

1919
@router.post("/add", response_model=CoreDatasource)
20-
async def add(session: SessionDep, ds: CoreDatasource):
20+
async def add(session: SessionDep, ds: CreateDatasource):
2121
return create_ds(session, ds)
2222

2323

backend/apps/datasource/crud/datasource.py

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
from sqlmodel import select
2-
from ..models.datasource import CoreDatasource, DatasourceConf
2+
from ..models.datasource import CoreDatasource, CreateDatasource, CoreTable, CoreField, ColumnSchema
33
import datetime
44
from common.core.deps import SessionDep
55
from apps.db.db import get_session, get_tables, get_fields, exec_sql
6-
from sqlalchemy import text
6+
from sqlalchemy import text, and_
7+
from common.utils.utils import deepcopy_ignore_extra
8+
from typing import List
9+
from ..crud.table import delete_table_by_ds_id
10+
from ..crud.field import delete_field_by_ds_id
711

812

913
def get_datasource_list(session: SessionDep) -> CoreDatasource:
@@ -26,21 +30,30 @@ def check_status(session: SessionDep, ds: CoreDatasource):
2630
return False
2731

2832

29-
def create_ds(session: SessionDep, ds: CoreDatasource):
33+
def create_ds(session: SessionDep, create_ds: CreateDatasource):
34+
ds = CoreDatasource()
35+
deepcopy_ignore_extra(create_ds, ds)
3036
ds.create_time = datetime.datetime.now()
3137
status = check_status(session, ds)
3238
ds.status = "Success" if status is True else "Fail"
3339
record = CoreDatasource(**ds.model_dump())
34-
# get tables and fields
35-
if status:
36-
pass
3740
session.add(record)
41+
session.flush()
42+
session.refresh(record)
43+
ds.id = record.id
3844
session.commit()
45+
46+
# save tables and fields
47+
if status:
48+
sync_table(session, ds, create_ds.tables)
49+
3950
return ds
4051

4152

4253
def update_ds(session: SessionDep, ds: CoreDatasource):
4354
ds.id = int(ds.id)
55+
status = check_status(session, ds)
56+
ds.status = "Success" if status is True else "Fail"
4457
record = session.exec(select(CoreDatasource).where(CoreDatasource.id == ds.id)).first()
4558
update_data = ds.model_dump(exclude_unset=True)
4659
for field, value in update_data.items():
@@ -54,6 +67,8 @@ def delete_ds(session: SessionDep, id: int):
5467
term = session.exec(select(CoreDatasource).where(CoreDatasource.id == id)).first()
5568
session.delete(term)
5669
session.commit()
70+
delete_table_by_ds_id(session, id)
71+
delete_field_by_ds_id(session, id)
5772
return {
5873
"message": f"Datasource with ID {id} deleted successfully."
5974
}
@@ -74,3 +89,65 @@ def getFields(session: SessionDep, id: int, table_name: str):
7489
def execSql(session: SessionDep, id: int, sql: str):
7590
ds = session.exec(select(CoreDatasource).where(CoreDatasource.id == id)).first()
7691
return exec_sql(ds, sql)
92+
93+
94+
def sync_table(session: SessionDep, ds: CoreDatasource, tables: List[CoreTable]):
95+
id_list = []
96+
for item in tables:
97+
statement = select(CoreTable).where(and_(CoreTable.ds_id == ds.id, CoreTable.table_name == item.table_name))
98+
record = session.exec(statement).first()
99+
# update exist table, only update table_comment
100+
if record is not None:
101+
item.id = record.id
102+
id_list.append(record.id)
103+
104+
record.table_comment = item.table_comment
105+
session.add(record)
106+
session.commit()
107+
else:
108+
# save new table
109+
table = CoreTable(ds_id=ds.id, checked=True, table_name=item.table_name, table_comment=item.table_comment,
110+
custom_comment=item.table_comment)
111+
session.add(table)
112+
session.flush()
113+
session.refresh(table)
114+
item.id = table.id
115+
id_list.append(table.id)
116+
session.commit()
117+
118+
# sync field
119+
fields = getFields(session, ds.id, item.table_name)
120+
sync_fields(session, ds, item, fields)
121+
122+
if len(id_list) > 0:
123+
session.query(CoreTable).filter(and_(CoreTable.ds_id == ds.id, CoreTable.id.not_in(id_list))).delete(
124+
synchronize_session=False)
125+
126+
127+
def sync_fields(session: SessionDep, ds: CoreDatasource, table: CoreTable, fields: List[ColumnSchema]):
128+
id_list = []
129+
for item in fields:
130+
statement = select(CoreField).where(
131+
and_(CoreField.table_id == table.id, CoreField.field_name == item.fieldName))
132+
record = session.exec(statement).first()
133+
if record is not None:
134+
item.id = record.id
135+
id_list.append(record.id)
136+
137+
record.field_comment = item.fieldComment
138+
session.add(record)
139+
session.commit()
140+
else:
141+
field = CoreField(ds_id=ds.id, table_id=table.id, checked=True, field_name=item.fieldName,
142+
field_type=item.fieldType, field_comment=item.fieldComment,
143+
custom_comment=item.fieldComment)
144+
session.add(field)
145+
session.flush()
146+
session.refresh(field)
147+
item.id = field.id
148+
id_list.append(field.id)
149+
session.commit()
150+
151+
if len(id_list) > 0:
152+
session.query(CoreField).filter(and_(CoreField.table_id == table.id, CoreField.id.not_in(id_list))).delete(
153+
synchronize_session=False)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from common.core.deps import SessionDep
2+
from ..models.datasource import CoreDatasource, CreateDatasource, CoreTable, CoreField, ColumnSchema
3+
4+
5+
def delete_field_by_ds_id(session: SessionDep, id: int):
6+
session.query(CoreField).filter(CoreField.ds_id == id).delete(synchronize_session=False)
7+
session.commit()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from common.core.deps import SessionDep
2+
from ..models.datasource import CoreDatasource, CreateDatasource, CoreTable, CoreField, ColumnSchema
3+
4+
5+
def delete_table_by_ds_id(session: SessionDep, id: int):
6+
session.query(CoreTable).filter(CoreTable.ds_id == id).delete(synchronize_session=False)
7+
session.commit()

backend/apps/datasource/models/datasource.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from sqlalchemy import Column, Text, BigInteger, DateTime, Integer, Identity
33
from datetime import datetime
44
from pydantic import BaseModel
5-
from typing import List
5+
from typing import List, Optional
66

77

88
class CoreDatasource(SQLModel, table=True):
@@ -17,20 +17,39 @@ class CoreDatasource(SQLModel, table=True):
1717
status: str = Field(max_length=64, nullable=True)
1818

1919

20-
class CoreTable(BaseModel):
21-
checked: str = True
22-
table_name: str = ''
23-
table_comment: str = ''
24-
custom_comment: str = ''
25-
table_fields: List = []
20+
class CoreTable(SQLModel, table=True):
21+
__tablename__ = "core_table"
22+
id: int = Field(sa_column=Column(Integer, Identity(always=True), nullable=False, primary_key=True))
23+
ds_id:int = Field(sa_column=Column(BigInteger()))
24+
checked: bool = Field(default=True)
25+
table_name: str = Field(sa_column=Column(Text))
26+
table_comment: str = Field(sa_column=Column(Text))
27+
custom_comment: str = Field(sa_column=Column(Text))
28+
29+
30+
class CoreField(SQLModel, table=True):
31+
__tablename__ = "core_field"
32+
id: int = Field(sa_column=Column(Integer, Identity(always=True), nullable=False, primary_key=True))
33+
ds_id: int = Field(sa_column=Column(BigInteger()))
34+
table_id: int = Field(sa_column=Column(BigInteger()))
35+
checked: bool = Field(default=True)
36+
field_name: str = Field(sa_column=Column(Text))
37+
field_type: str = Field(max_length=128, nullable=True)
38+
field_comment: str = Field(sa_column=Column(Text))
39+
custom_comment: str = Field(sa_column=Column(Text))
2640

2741

28-
class CoreField(BaseModel):
29-
checked: str = True
30-
field_name: str = ''
31-
field_type: str = ''
32-
field_comment: str = ''
33-
custom_comment: str = ''
42+
# datasource create obj
43+
class CreateDatasource(BaseModel):
44+
id: int = None
45+
name: str = ''
46+
description: str = ''
47+
type: str = ''
48+
configuration: str = ''
49+
create_time: Optional[datetime] = None
50+
create_by: int = 0
51+
status: str = ''
52+
tables: List[CoreTable] = []
3453

3554

3655
class DatasourceConf(BaseModel):

backend/common/utils/utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,13 @@ def verify_password_reset_token(token: str) -> str | None:
3737
return str(decoded_token["sub"])
3838
except InvalidTokenError:
3939
return None
40+
41+
42+
def deepcopy_ignore_extra(src, dest):
43+
import copy
44+
for attr in vars(src):
45+
if hasattr(dest, attr):
46+
src_value = getattr(src, attr)
47+
dest_value = copy.deepcopy(src_value) # deep copy
48+
setattr(dest, attr, dest_value)
49+
return dest

frontend/src/views/ds/form.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ const save = async(formEl: FormInstance | undefined) => {
137137
emit('refresh')
138138
})
139139
} else {
140+
form.value.tables = [{"table_name":"core_dataset_table"},{"table_name":"pre_user"}]
140141
datasourceApi.add(form.value).then((res: any) => {
141142
console.log(res)
142143
close()

0 commit comments

Comments
 (0)