Skip to content

Commit 41aa5d6

Browse files
committed
Merge branch 'main' into manytoone
2 parents bb122df + 081456b commit 41aa5d6

File tree

42 files changed

+694
-851
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+694
-851
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![Python package](https://github.com/Chise1/fast-tmp/actions/workflows/test.yml/badge.svg)](https://github.com/Chise1/fast-tmp/actions/workflows/test.yml)
44
[![codecov](https://codecov.io/gh/Chise1/fast-tmp/branch/main/graph/badge.svg?token=7CZE532R0H)](https://codecov.io/gh/Chise1/fast-tmp)
5-
[![Documentation Status](https://readthedocs.org/projects/fast-tmp/badge/?version=latest)](https://fast-tmp.readthedocs.io/zh_TW/latest/?badge=latest)
5+
[![Documentation Status](https://readthedocs.org/projects/fast-tmp/badge/?version=latest)](https://fast-tmp.readthedocs.io/?badge=latest)
66
![GitHub](https://img.shields.io/github/license/Chise1/fast-tmp)
77

88
# 介绍
@@ -14,7 +14,7 @@ fast-tmp项目受django-admin的影响,旨在实现一个基于sqlalchemy+fast
1414
- amis:一款利用json数据生成页面的前端低代码项目。
1515

1616
笔者前端能力比较弱,从实用主义出发,利用amis搭建后台管理的页面。这也为未来页面的功能拓展提供了无限可能。并摆脱前端开发的影响。(由于偷懒,登陆页面用的taber构建的。以后有时间了修改)
17-
17+
更多内容查看[教程](https://fast-tmp.readthedocs.io/)
1818
## 示例
1919

2020
url: http://124.222.119.206:8000/admin

docs/ModelAdmin.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# ModelAdmin类说明
2+
3+
该类是整个项目的核心类,主要用于生成前端页面。 方法如下:
4+
5+
```python
6+
from typing import Any, Tuple
7+
from sqlalchemy import Column
8+
9+
10+
class ModelAdmin:
11+
model: Any # sqlalchemy的model
12+
# list
13+
list_display: Tuple[Column, ...] = () # 在前端显示的内容,注意,如果要支持修改和删除功能,这里必须有类的主键
14+
list_per_page: int = 10 # 每页显示数量
15+
list_max_show_all: int = 200 # 最大每页数量
16+
# create
17+
create_fields: Tuple[Column, ...] = () # 创建页面需要的字段,如果有关系型字段(多对多,多对一,一对多,一对一等)则不要使用关系字段对应的外键字段,目前暂时只支持多对一和多对多
18+
update_fields: Tuple[Column, ...] = () # 同create_fields
19+
methods: Tuple[str, ...] = ("list", "create", "update", "delete",) # 支持的方法
20+
21+
@classmethod
22+
def get_create_dialogation_button(cls): # 新增按钮,如果要自定义按钮可重构这里
23+
...
24+
25+
@classmethod
26+
def get_list_page(cls): # list页面展示的字段
27+
...
28+
29+
@classmethod
30+
def pks(cls): # 获取主键
31+
...
32+
33+
@classmethod
34+
def get_del_one_button(cls): # 删除按钮
35+
...
36+
37+
@classmethod
38+
def get_update_one_button(cls): # 更新按钮和页面
39+
...
40+
41+
@classmethod
42+
def get_operation(cls): # 生成每条数据后面的按钮面板,要增加自定义按钮可以在这里增加
43+
...
44+
45+
@classmethod
46+
def get_crud(cls): # 获取整个页面的json模型
47+
...
48+
49+
@classmethod
50+
def get_app_page(cls): # 获取页面
51+
...
52+
53+
@classmethod
54+
def create_model(cls, data: dict):
55+
"""
56+
写入数据库之前调用
57+
"""
58+
...
59+
60+
@classmethod
61+
def update_model(cls, instance: Any, data: dict) -> Any:
62+
"""
63+
更新数据之前调用
64+
"""
65+
...
66+
67+
@classmethod
68+
def get_list_sql(cls): # 获取显示list的sql
69+
...
70+
71+
@classmethod
72+
def get_one_sql(cls, pks: list): # 获取update使用的sql
73+
...
74+
```

docs/cli.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# fast-tmp脚本指令说明
2+
3+
## createsupuser
4+
5+
创建一个超级用户,用法如下:
6+
7+
```shell
8+
fast-tmp createsuperuser username password
9+
```
10+
11+
## 初始化一个项目
12+
13+
通过自定义的模板进行,需要安装cookiecutter,或者在安装fast-tmp的时候执行```pip install fast-tmp[cli]```
14+
15+
然后可以执行```fast-tmp startproject```即可创建一个项目的基本结构。
16+
17+
## 自定义脚本
18+
19+
在settings.py里面引入自定义脚本所在的路径,即可自动读取并注册到脚本里面去, 例如:
20+
21+
```python
22+
# cli.py
23+
def hello():
24+
"""
25+
你好.
26+
"""
27+
print("hello world.")
28+
29+
30+
# settings.py
31+
EXTRA_SCRIPT = ['cli.hello'] # 自定义执行脚本
32+
33+
```
34+
35+
然后执行```fast-tmp```即可查看到自己注册的方法。

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![Python package](https://github.com/Chise1/fast-tmp/actions/workflows/test.yml/badge.svg)](https://github.com/Chise1/fast-tmp/actions/workflows/test.yml)
44
[![codecov](https://codecov.io/gh/Chise1/fast-tmp/branch/main/graph/badge.svg?token=7CZE532R0H)](https://codecov.io/gh/Chise1/fast-tmp)
5-
[![Documentation Status](https://readthedocs.org/projects/fast-tmp/badge/?version=latest)](https://fast-tmp.readthedocs.io/zh_TW/latest/?badge=latest)
5+
[![Documentation Status](https://readthedocs.org/projects/fast-tmp/badge/?version=latest)](https://fast-tmp.readthedocs.io/?badge=latest)
66
![GitHub](https://img.shields.io/github/license/Chise1/fast-tmp)
77

88
# 介绍

example/app.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
from fast_tmp.admin.server import admin
99
from example.factory import create_app
1010
from fast_tmp.site import register_model_site
11-
from example.admin import UserInfoAdmin
11+
from example.admin import UserInfoAdmin, AuthorAdmin, BookAdmin
1212

13-
register_model_site({"Example":[UserInfoAdmin]})
13+
register_model_site({"Example": [UserInfoAdmin, AuthorAdmin, BookAdmin]})
1414
app: FastAPI = create_app()
1515
app.mount("/admin", admin, name="admin", )
1616
Base.metadata.create_all(engine)
1717

1818
if __name__ == '__main__':
1919
import uvicorn
20+
2021
uvicorn.run(app, host="0.0.0.0", port=8000, lifespan="on")

example/example/admin.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
11
from fast_tmp.site import ModelAdmin
2-
from .models import UserInfo
2+
from .models import UserInfo, Author, Book
33

44

55
class UserInfoAdmin(ModelAdmin):
66
model = UserInfo
77
create_fields = [UserInfo.name, UserInfo.age, UserInfo.birthday, UserInfo.money, UserInfo.height, UserInfo.info,
8+
UserInfo.tag, UserInfo.is_superuser]
9+
update_fields = create_fields
10+
list_display = [UserInfo.id, UserInfo.name, UserInfo.age, UserInfo.birthday, UserInfo.money, UserInfo.height,
11+
UserInfo.info,
812
UserInfo.tag, UserInfo.is_superuser]
13+
14+
15+
class AuthorAdmin(ModelAdmin):
16+
model = Author
17+
create_fields = [Author.name]
18+
list_display = create_fields
19+
20+
21+
class BookAdmin(ModelAdmin):
22+
model = Book
23+
create_fields = [Book.name, Book.author] # todo 增加提醒,可以为关系,可以为id
24+
list_display = [Book.id, Book.name, Book.author] # todo 增加检查,必须在listplay里面带主键
925
update_fields = create_fields
10-
list_display = [UserInfo.id,UserInfo.name, UserInfo.age, UserInfo.birthday, UserInfo.money, UserInfo.height, UserInfo.info,
11-
UserInfo.tag, UserInfo.is_superuser]

example/example/models.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,37 @@
11
import time
22

3-
from sqlalchemy import String, Boolean, Integer, DateTime, DECIMAL, Float, JSON, Text, Time, Column
3+
from sqlalchemy import String, Boolean, Integer, DateTime, DECIMAL, Float, JSON, Text, Time, Column, ForeignKey
44
# todo Image,File
5+
from sqlalchemy.orm import relationship
6+
57
from fast_tmp.models import Base
68

79

810
class UserInfo(Base):
911
__tablename__ = "userinfo"
1012
id = Column(Integer, primary_key=True)
1113
name = Column(String(128), unique=True)
12-
age = Column(Integer,default=10,)
13-
birthday=Column(DateTime)
14-
money=Column(DECIMAL(scale=3))
15-
height=Column(Float)
16-
info=Column(JSON)
17-
tag=Column(Text)
14+
age = Column(Integer, default=10, )
15+
birthday = Column(DateTime)
16+
money = Column(DECIMAL(scale=3))
17+
height = Column(Float)
18+
info = Column(JSON)
19+
tag = Column(Text)
1820
# create_time=Column(Time,default=time.time)
1921
is_superuser = Column(Boolean(), default=True)
22+
23+
24+
class Author(Base):
25+
__tablename__ = "author"
26+
id = Column(Integer, primary_key=True)
27+
name = Column(String(32),nullable=False)
28+
# books: Author = relationship("Author", back_populates="books")
29+
30+
31+
class Book(Base):
32+
__tablename__ = "book"
33+
34+
id = Column(Integer, primary_key=True)
35+
name = Column(String(128), nullable=False)
36+
author_id = Column(Integer, ForeignKey('author.id'), info={"admin_name": "name"},nullable=False) # todo 增加提示
37+
author: Author = relationship("Author", backref="books")

fast_tmp/admin/endpoint.py

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
1+
from datetime import datetime
12
from typing import List, Optional
23

34
from fastapi import APIRouter, Depends
5+
from fastapi.params import Path
46
from pydantic import BaseModel
5-
from sqlalchemy import delete, func, select
7+
from sqlalchemy import (
8+
DECIMAL,
9+
INTEGER,
10+
BigInteger,
11+
DateTime,
12+
Float,
13+
Integer,
14+
Numeric,
15+
SmallInteger,
16+
delete,
17+
func,
18+
select,
19+
)
620
from sqlalchemy.orm import Session
721
from starlette.requests import Request
822
from starlette.responses import RedirectResponse
@@ -41,10 +55,10 @@ def list_view(
4155
):
4256
if not user:
4357
return RedirectResponse(request.url_for("admin:login"))
44-
45-
datas = session.execute(
46-
select(page_model.list_display).limit(perPage).offset((page - 1) * perPage)
47-
)
58+
sql = page_model.get_list_sql()
59+
if sql == None:
60+
return BaseRes()
61+
datas = session.execute(sql.limit(perPage).offset((page - 1) * perPage))
4862
items = []
4963
for data in datas:
5064
items.append(dict(data))
@@ -70,7 +84,7 @@ def update_data(
7084
):
7185
if not user:
7286
return RedirectResponse(request.url_for("admin:login"))
73-
data = clean_data_to_model(page_model.update_fields, data)
87+
data = clean_data_to_model(page_model.get_clean_fields(page_model.update_fields), data)
7488
w = get_pks(page_model, request)
7589
if isinstance(w, BaseRes):
7690
return w
@@ -92,10 +106,10 @@ def update_view(
92106
if not user:
93107
return RedirectResponse(request.url_for("admin:login"))
94108

95-
w = get_pks(page_model, request)
96-
if isinstance(w, BaseRes):
97-
return w
98-
data = session.execute(select(page_model.update_fields).where(*w)).fetchone()
109+
pks = get_pks(page_model, request)
110+
if isinstance(pks, BaseRes):
111+
return pks
112+
data = session.execute(page_model.get_one_sql(pks)).fetchone()
99113
if not data:
100114
return not_found_instance
101115
return BaseRes(data=dict(data))
@@ -138,12 +152,23 @@ def delete_one(
138152

139153

140154
def get_pks(page_model: ModelAdmin, request: Request):
155+
"""
156+
获取要查询的单个instance的主键
157+
"""
141158
params = dict(request.query_params)
142159
pks = get_pk(page_model.model)
143160
w = []
144161
for k, v in params.items():
145162
if pks.get(k) is not None:
146-
w.append(pks[k] == v)
163+
field = pks[k]
164+
if isinstance(
165+
field.type, (Integer, DECIMAL, BigInteger, Float, INTEGER, Numeric, SmallInteger)
166+
):
167+
w.append(pks[k] == int(v))
168+
elif isinstance(field.type, DateTime):
169+
w.append(pks[k] == datetime.strptime(v, "%Y-%m-%dT%H:%M:%S"))
170+
else:
171+
w.append(pks[k] == v)
147172
else:
148173
return key_error
149174
return w
@@ -173,3 +198,29 @@ def get_schema(
173198
return RedirectResponse(request.url_for("admin:login"))
174199

175200
return BaseRes(data=page.get_app_page())
201+
202+
203+
@router.get("/{resource}/selects/{field}")
204+
def get_selects(
205+
request: Request,
206+
field: str = Path(...), # type: ignore
207+
page_model: ModelAdmin = Depends(get_model_site),
208+
user: Optional[User] = Depends(decode_access_token_from_data),
209+
session: Session = Depends(get_db_session),
210+
perPage: int = 10,
211+
page: int = 1,
212+
):
213+
source_field = getattr(page_model.model, field)
214+
relation_model = source_field.property.mapper.class_
215+
216+
datas = session.execute(
217+
select(list(get_pk(relation_model).values())).limit(perPage).offset((page - 1) * perPage)
218+
)
219+
items = []
220+
for data in datas:
221+
items.append(dict(data))
222+
s = session.execute(select(func.count()).select_from(relation_model))
223+
total = 0
224+
for i in s:
225+
total = i[0]
226+
return BaseRes(data={"total": total, "rows": items})

fast_tmp/admin/schema/zs.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
展示页面需要的model
3+
"""
4+
from typing import List
5+
6+
from pydantic import BaseModel
7+
8+
from fast_tmp.admin.schema.forms import Column
9+
10+
11+
class ZSTable(BaseModel):
12+
type: str = "table"
13+
title: str
14+
name: str
15+
source: str
16+
columns: List[Column]
-6.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)