Skip to content

Commit c9e7848

Browse files
committed
完成多对多,多对一的修改。
1 parent e86dfab commit c9e7848

File tree

14 files changed

+271
-103
lines changed

14 files changed

+271
-103
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
checkfiles = fast_tmp/
1+
checkfiles = fast_tmp/ tests/
22
black_opts = -l 100 -t py38
33
py_warn = PYTHONDEVMODE=1
44

docs/ModelAdmin.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ from sqlalchemy import Column
88

99

1010
class ModelAdmin:
11-
model: Any # sqlalchemy的model
11+
model: Any # sqlalchemy的model,如果model存在多对多字段,则model暂时只支持单主键
1212
# list
1313
list_display: Tuple[Column, ...] = () # 在前端显示的内容,注意,如果要支持修改和删除功能,这里必须有类的主键
1414
list_per_page: int = 10 # 每页显示数量

example/example/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class UserInfoAdmin(ModelAdmin):
1414

1515
class AuthorAdmin(ModelAdmin):
1616
model = Author
17-
create_fields = [Author.name]
17+
create_fields = [Author.name, Author.books]
1818
list_display = create_fields
1919

2020

example/example/models.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import time
2+
from typing import List
23

34
from sqlalchemy import String, Boolean, Integer, DateTime, DECIMAL, Float, JSON, Text, Time, Column, ForeignKey
45
# todo Image,File
@@ -25,7 +26,7 @@ class Author(Base):
2526
__tablename__ = "author"
2627
id = Column(Integer, primary_key=True)
2728
name = Column(String(32),nullable=False)
28-
# books: Author = relationship("Author", back_populates="books")
29+
books: List['Book'] = relationship("Book", back_populates="author")
2930

3031

3132
class Book(Base):
@@ -34,4 +35,4 @@ class Book(Base):
3435
id = Column(Integer, primary_key=True)
3536
name = Column(String(128), nullable=False)
3637
author_id = Column(Integer, ForeignKey('author.id'), info={"admin_name": "name"},nullable=False) # todo 增加提示
37-
author: Author = relationship("Author", backref="books")
38+
author: Author = relationship("Author", back_populates="books")

fast_tmp/admin/endpoint.py

Lines changed: 93 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,20 @@
1515
SmallInteger,
1616
delete,
1717
func,
18+
inspect,
1819
select,
1920
)
20-
from sqlalchemy.orm import Session
21+
from sqlalchemy.orm import MANYTOMANY, RelationshipProperty, Session
2122
from starlette.requests import Request
2223
from starlette.responses import RedirectResponse
2324

25+
from fast_tmp.responses import BaseRes, key_error, not_found_instance, single_pk
2426
from fast_tmp.site import ModelAdmin, get_model_site
2527

2628
from ..db import get_db_session
2729
from ..models import User
2830
from ..site.utils import clean_data_to_model, get_pk
2931
from .depends import decode_access_token_from_data
30-
from .responses import BaseRes, key_error, not_found_instance
3132

3233
router = APIRouter()
3334

@@ -85,13 +86,13 @@ def update_data(
8586
if not user:
8687
return RedirectResponse(request.url_for("admin:login"))
8788
data = clean_data_to_model(page_model.get_clean_fields(page_model.update_fields), data)
88-
w = get_pks(page_model, request)
89+
w = get_pks(page_model.model, request)
8990
if isinstance(w, BaseRes):
9091
return w
9192
old_data = session.execute(select(page_model.model).where(*w)).scalar_one_or_none()
9293
if not old_data:
9394
return not_found_instance
94-
page_model.update_model(old_data, data)
95+
page_model.update_model(old_data, data, session)
9596
session.commit()
9697
return BaseRes()
9798

@@ -106,13 +107,29 @@ def update_view(
106107
if not user:
107108
return RedirectResponse(request.url_for("admin:login"))
108109

109-
pks = get_pks(page_model, request)
110+
pks = get_pks(page_model.model, request)
110111
if isinstance(pks, BaseRes):
111112
return pks
112-
data = session.execute(page_model.get_one_sql(pks)).fetchone()
113+
data = session.execute(select(page_model.model).where(*pks)).scalar_one_or_none()
113114
if not data:
114115
return not_found_instance
115-
return BaseRes(data=dict(data))
116+
res = {}
117+
for i in page_model.update_fields:
118+
if isinstance(i.property, RelationshipProperty):
119+
prop = i.property
120+
if prop.direction in (MANYTOMANY,): # TODO need onetomany
121+
pk = list(get_pk(prop.entity.class_).keys())[0] # 只支持单主键
122+
# subs: str = getattr(data, i.key, "") # type: ignore
123+
# if not subs:
124+
# raise not_found_model
125+
# else:
126+
# # for sub in subs:
127+
# # pk_v=getattr(sub,pk)
128+
res[i.key] = [getattr(sub, pk) for sub in getattr(data, i.key)] # type: ignore
129+
else:
130+
res[i.key] = getattr(data, i.key) # type: ignore
131+
132+
return BaseRes(data=res)
116133

117134

118135
@router.post("/{resource}/create")
@@ -127,7 +144,7 @@ def create(
127144
return RedirectResponse(request.url_for("admin:login"))
128145

129146
data = clean_data_to_model(page_model.create_fields, data)
130-
instance = page_model.create_model(data)
147+
instance = page_model.create_model(data, session)
131148
session.add(instance)
132149
session.commit()
133150
return BaseRes(data=data)
@@ -143,20 +160,31 @@ def delete_one(
143160
if not user:
144161
return RedirectResponse(request.url_for("admin:login"))
145162

146-
w = get_pks(page_model, request)
163+
w = get_pks(page_model.model, request)
147164
if isinstance(w, BaseRes):
148165
return w
149166
session.execute(delete(page_model.model).where(*w))
150167
session.commit()
151168
return BaseRes()
152169

153170

154-
def get_pks(page_model: ModelAdmin, request: Request):
171+
def clean_param(field_type, param: str):
172+
if isinstance(
173+
field_type, (Integer, DECIMAL, BigInteger, Float, INTEGER, Numeric, SmallInteger)
174+
):
175+
return int(param)
176+
elif isinstance(field_type, DateTime):
177+
return datetime.strptime(param, "%Y-%m-%dT%H:%M:%S")
178+
else:
179+
return param
180+
181+
182+
def get_pks(model, request: Request):
155183
"""
156184
获取要查询的单个instance的主键
157185
"""
158186
params = dict(request.query_params)
159-
pks = get_pk(page_model.model)
187+
pks = get_pk(model)
160188
w = []
161189
for k, v in params.items():
162190
if pks.get(k) is not None:
@@ -210,9 +238,62 @@ def get_selects(
210238
perPage: int = 10,
211239
page: int = 1,
212240
):
241+
mapper = inspect(page_model.model)
242+
items = []
243+
total = 0
244+
for attr in mapper.attrs:
245+
if attr.key == field:
246+
relation_model = attr.entity.class_
247+
secondary = attr.secondary
248+
for col in secondary.foreign_key_constraints:
249+
if col.referred_table in mapper.tables:
250+
params = dict(request.query_params)
251+
if len(params) > 1:
252+
raise single_pk
253+
col_name = col.column_keys[0]
254+
for c in secondary.c:
255+
if c.key == col_name:
256+
clean_value = clean_param(c.type, list(params.values())[0])
257+
sql = (
258+
select(*list(get_pk(relation_model).values()))
259+
.join(secondary)
260+
.where(c == clean_value)
261+
.limit(perPage)
262+
.offset((page - 1) * perPage)
263+
)
264+
datas = session.execute(sql)
265+
total_f = session.execute(
266+
select(func.count())
267+
.select_from(relation_model)
268+
.join(secondary)
269+
.where(col == list(params.values())[0])
270+
).fetchone()
271+
if total_f is not None:
272+
total = total_f[0]
273+
for data in datas:
274+
items.append(dict(data))
275+
return BaseRes(data={"total": total, "rows": items})
276+
277+
278+
@router.get("/{resource}/picks/{field}")
279+
def get_picks(
280+
request: Request,
281+
field: str = Path(...), # type: ignore
282+
page_model: ModelAdmin = Depends(get_model_site),
283+
user: Optional[User] = Depends(decode_access_token_from_data),
284+
session: Session = Depends(get_db_session),
285+
perPage: int = 10,
286+
page: int = 1,
287+
):
288+
"""
289+
枚举选择
290+
"""
213291
source_field = getattr(page_model.model, field)
214-
relation_model = source_field.property.mapper.class_
292+
if isinstance(source_field.property, RelationshipProperty):
293+
relation_model = source_field.property.mapper.class_
215294

295+
else:
296+
relation_model = source_field.property.mapper.class_
216297
datas = session.execute(
217298
select(list(get_pk(relation_model).values())).limit(perPage).offset((page - 1) * perPage)
218299
)

fast_tmp/admin/schema/frame.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Dialog(BaseModel):
1212
nextCondition: bool = True
1313
size: DialogSize = DialogSize.md
1414
actions: Optional[List[_Action]]
15-
body: Union[str, BaseModel]
15+
body: Union[str, BaseModel, dict]
1616

1717

1818
# todo:尚未完成:https://baidu.gitee.io/amis/docs/components/dialog?page=1

fast_tmp/admin/schema/zs.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,9 @@ class ZSTable(BaseModel):
1414
name: str
1515
source: str
1616
columns: List[Column]
17+
18+
19+
class ZS(BaseModel):
20+
type: str = "service"
21+
api: str
22+
body: BaseModel

fast_tmp/admin/server.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
from fast_tmp.conf import settings
1313
from fast_tmp.db import get_db_session
1414
from fast_tmp.models import User
15-
from fast_tmp.models.site import UserAdmin
15+
from fast_tmp.models.site import GroupAdmin, UserAdmin
16+
from fast_tmp.responses import BaseRes
1617
from fast_tmp.site import model_list, register_model_site
1718
from fast_tmp.utils.token import create_access_token
1819

1920
from ..jinja_extension.tags import register_tags
2021
from .constant import crud_root_rooter, model_router
2122
from .endpoint import router
22-
from .responses import BaseRes
2323

2424
base_path = os.path.dirname(__file__)
2525
templates = Jinja2Templates(directory=base_path + "/templates")
@@ -30,7 +30,7 @@
3030
else:
3131
admin.mount("/static", app=StaticFiles(directory=base_path + "/static"), name="static")
3232

33-
register_model_site({"Auth": [UserAdmin]})
33+
register_model_site({"Auth": [UserAdmin, GroupAdmin]})
3434
admin.include_router(router, prefix=model_router)
3535

3636

fast_tmp/models/site.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from typing import Any
22

3+
from sqlalchemy.orm import Session
4+
35
from fast_tmp.models import Group, User
46
from fast_tmp.site import ModelAdmin
57

@@ -11,19 +13,20 @@ class UserAdmin(ModelAdmin):
1113
update_fields = (User.password,)
1214

1315
@classmethod
14-
def create_model(cls, data: dict) -> Any:
15-
user = super().create_model(data)
16+
def create_model(cls, data: dict, session: Session) -> Any:
17+
user = super().create_model(data, session)
1618
user.set_password(data["password"])
1719
return user
1820

1921
@classmethod
20-
def update_model(cls, model: User, data: dict) -> Any:
22+
def update_model(cls, model: User, data: dict, session: Session) -> Any:
23+
super().update_model(model, data, session)
2124
model.set_password(data["password"])
2225
return model
2326

2427

2528
class GroupAdmin(ModelAdmin):
2629
model = Group
27-
list_display = (Group.id, Group.name)
28-
create_fields = (Group.name,)
29-
update_fields = (Group.name,)
30+
list_display = (Group.id, Group.name, Group.users)
31+
create_fields = (Group.name, Group.users)
32+
update_fields = (Group.name, Group.users)

fast_tmp/responses.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from typing import Any
2+
23
from fastapi import HTTPException
34
from pydantic import BaseModel
45

@@ -11,7 +12,5 @@ class BaseRes(BaseModel):
1112

1213
key_error = BaseRes(status=400, msg="主键错误")
1314
not_found_instance = BaseRes(status=404, msg="找不到对象")
14-
not_found_model = HTTPException(
15-
status_code=200,
16-
detail=BaseRes(msg="找不到对象").dict()
17-
)
15+
not_found_model = HTTPException(status_code=200, detail=BaseRes(msg="找不到对象").dict())
16+
single_pk = HTTPException(status_code=200, detail=BaseRes(msg="暂时只支持单主键").dict())

0 commit comments

Comments
 (0)