fastapi / kinit 笔记

目录

介绍:

1. 后端Api应用安装及初始化

1.1 数据库会话及连接参数设置

2. 数据库迁移或维护

3. VS Code中的项目launch.json

4. 日志 logger

- 打开sql日志

5. CRUD 新模块

6. 数据初始化与迁移

6.1 初始化:

6.2 模型应用到数据库 / 数据迁移:

7. 路由

8. PM模型时间字段本地化转换

9. Pydantic

9.1 验证器不能带入含db操作的验证逻辑, 因为db操作是异步的,Pydantic不支持, 需放到上一级服务中进行

9.2 为模型添加逻辑扩展字段

10 数据查询 

11. Kinit迁移上Postgresql

参考:


介绍:

Kinit 是一套开箱即用的中后台解决方案,可以作为新项目的启动模版,前后端分离架构,开箱即用,在线例子:https://kinit.ktianc.top/login

1. 后端Api应用安装及初始化

# 获取
git clone https://gitee.com/ktianc/kinit.git

# 安装依赖
cd kinit-api
pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/

# 全局环境配置
文件: application/settings.py
DEBUG = True
DEMO = False

# 子环境配置(数据库, 第三方)
文件:application/config/development.py or production.py

# 数据库模型映射配置(也需设置数据库用密)
文件:alembic.ini

# 初始化数据库数据:
# (生产环境)
python3 main.py init
# (开发环境)
python3 main.py init --env dev

# 启动
# 进入项目根目录下执行
python3 main.py run

默认账号:15020221010 密码:kinit2022 

1.1 数据库会话及连接参数设置
# 文件
core\database.py

# 关键方法
async def db_getter() -> AsyncGenerator[AsyncSession, None]:

# 连接参数
async_engine = create_async_engine(
    SQLALCHEMY_DATABASE_URL,
    echo=False,
    echo_pool=False,
    pool_pre_ping=True,
    pool_recycle=3600,
    pool_size=5,
    max_overflow=5,
    connect_args={}
)

2. 数据库迁移或维护

- 用的是alembic工具 Welcome to Alembic’s documentation! — Alembic 1.13.1 documentation

# 创建一个新的迁移
alembic --name dev revision -m "add age column"

编辑迁移文件

def upgrade():
    op.add_column('users', sa.Column('age', sa.Integer))

def downgrade():
    op.drop_column('users', 'age')

命令执行

alembic upgrade head

# 回滚上一版本
alembic downgrade -1

3. VS Code中的项目launch.json

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name":"Run Api",
            "type":"python",
            "request":"launch",
            "program":"kinit-api/main.py",
            "args":["run"],
            "console":"integratedTerminal",
            "justMyCode":true
        },
        {
            "command": "pnpm --prefix kinit-admin run dev",
            "name": "Run BO",
            "request": "launch",
            "type": "node-terminal"
        }
    ]
}

4. 日志 logger

采用的是loguru工具 (https://github.com/Delgan/loguru)

# 使用时引入
from core.logger import logger

# 记录信息
logger.info(f"sth")
- 打开sql日志

  编辑 core\crud.py, 在约179行加入logger.info(sql) 

if limit != 0:
   sql = sql.offset((page - 1) * limit).limit(limit)

queryset = await self.db.scalars(sql)

...
logger.info(sql)

...

5. CRUD 新模块

- 定义数据模型 apps\vadmin\iot\models\IotDevice.py

- 修改生成脚本 scripts\crud_generate\main.py

# 头部加入路径,不然引用application文件时会报找不到
sys.path.insert(0, os.path.join(os.path.abspath(os.path.dirname(__file__)), '../../'))

...

# 加入执行代码
if __name__ == '__main__':
    # from apps.vadmin.auth.models import VadminUser
    from apps.vadmin.iot.models.IotDevice import IotDevice

    crud = CrudGenerate(IotDevice, "设备", "IotDevice")
    # 只打印代码,不执行创建写入
    crud.generate_codes()
    # 创建并写入代码
    crud.main()

- 执行生成命令, 在apps/vadmin/iot 下生成相关文件

python scripts\crud_generate\main.py

- 在 application\urls.py 加入路由

{"ApiRouter": vadmin_iot_app, "prefix": "/vadmin/iot", "tags": ["IOT管理"]},

6. 数据初始化与迁移

获取代码后, 确认环境是 dev还是pro, 对应的是不同的数据环境

环境路径目录备注:以后生成的脚本路径
devalembic\versions_dev
proalembic\versions_dev
6.1 初始化:

Tip: 确保versions_dev/ 下没旧的或多余文件,否则会报 “ERROR [alembic.util.messaging] Target database is not up to date.” 错误

python main.py init --env dev
6.2 模型应用到数据库 / 数据迁移:

 - 如上建立 apps\vadmin\iot\models\IotDevice.py 模型

-  编辑 alembic\env.py,引入相关模型

from apps.vadmin.iot.models import *

- 执行 

python main.py migrate--env dev

7. 路由

 定义文件:application\urls.py,在main.py引入,子模块下的view.py申明api入口

8. PM模型时间字段本地化转换

class DeviceSimpleOut(Device):
    model_config = ConfigDict(from_attributes=True)

    id: int = Field(..., title="编号")
    create_datetime: DatetimeStr = Field(..., title="创建时间")
    update_datetime: DatetimeStr = Field(..., title="更新时间")
    
    @validator('create_datetime', 'update_datetime', pre=True)
    def convert_utc_to_local(cls, value):
        return utils.convert_utc_to_local(value)

utils.py

from pydantic import PositiveFloat, Field, EmailStr, validate_call
from datetime import datetime, timezone, timedelta
import pytz


def convert_utc_to_local(value):
        # 假设本地时区是东八区
        local_timezone = pytz.timezone('Asia/Shanghai')
        # 将原始的UTC时间转换为本地时间
        local_datetime = value.replace(tzinfo=timezone.utc).astimezone(local_timezone)
        return local_datetime

9. Pydantic

9.1 验证器不能带入含db操作的验证逻辑, 因为db操作是异步的,Pydantic不支持, 需放到上一级服务中进行
9.2 为模型添加逻辑扩展字段

可以利用rooot_validator装饰器为扩展字段赋值, 例子:

假设有字段model.is_bound (init 0/1),希望输出带 is_bound_label显示

class DeviceSimpleOut(Device):
    model_config = ConfigDict(from_attributes=True)

    id: int = Field(..., title="编号")
    create_datetime: DatetimeStr = Field(..., title="创建时间")
    update_datetime: DatetimeStr = Field(..., title="更新时间")

    is_bound_label: str | None

    logs: list[DevicelogSimpleOut] = []
    history_states: list[DevicestatehistorySimpleOut] = []

    @validator("create_datetime", "update_datetime", pre=True)
    def convert_utc_to_local(cls, value):
        return utils.convert_utc_to_local(value)

    @root_validator(pre=True)
    def labels(cls, values):
        values.is_bound_label = "是" if (values.is_bound == 1) else "否"
        return values

10 数据查询 

- kinit 自定义的查询过滤规则与写法约定

## 查询数据

### 自定义的一些查询过滤

```python
# 日期查询
# 值的类型:str
# 值得格式:%Y-%m-%d:2023-05-14
字段名称=("date", 值)

# 模糊查询
# 值的类型: str
字段名称=("like", 值)

# in 查询
# 值的类型:list
字段名称=("in", 值)

# 时间区间查询
# 值的类型:tuple 或者 list
字段名称=("between", 值)

# 月份查询
# 值的类型:str
# 值的格式:%Y-%m:2023-05
字段名称=("month", 值)

# 不等于查询
字段名称=("!=", 值)

# 大于查询
字段名称=(">", 值)

# 等于 None
字段名称=("None")

# 不等于 None
字段名称=("not None")
```

示例:

查询所有用户id为1或2或 4或6,并且email不为空,并且名称包括李:

```python
users = UserDal(db).get_datas(limit=0, id=("in", [1,2,4,6]), email=("not None", ), name=("like", "李"))

# limit=0:表示返回所有结果数据
# 这里的 get_datas 默认返回的是 pydantic 模型数据
# 如果需要返回用户对象列表,使用如下语句:
users = UserDal(db).get_datas(
    limit=0,
    id=("in", [1,2,4,6]),
    email=("not None", ),
    name=("like", "李"),
    v_return_objs=True
)
```

查询所有用户id为1或2或 4或6,并且email不为空,并且名称包括李:

查询第一页,每页两条数据,并返回总数,同样可以通过 `get_datas` 实现原始查询方式:

```python
v_where = [VadminUser.id.in_([1,2,4,6]), VadminUser.email.isnot(None), VadminUser.name.like(f"%李%")]
users, count = UserDal(db).get_datas(limit=2, v_where=v_where, v_return_count=True)

# 这里的 get_datas 默认返回的是 pydantic 模型数据
# 如果需要返回用户对象列表,使用如下语句:
users, count = UserDal(db).get_datas(
    limit=2,
    v_where=v_where,
    v_return_count=True
    v_return_objs=True
)
```

### 外键查询示例

以常见问题表为主表,查询出创建用户名称为kinit的用户,创建了哪些常见问题,并加载出用户信息:

```python
v_options = [joinedload(VadminIssue.create_user)]
v_join = [["create_user"]]
v_where = [VadminUser.name == "kinit"]
datas = await crud.IssueCategoryDal(auth.db).get_datas(
    limit=0,
    v_options=options,
    v_join=v_join,
    v_where=v_where,
    v_return_objs=True
)
```

11. Kinit迁移上Postgresql

初始化数据时有两个地方需要修改:
apps\vadmin\auth\crud.py 行586: 
self.model.disabled == 0 => self.model.disabled == false()

apps\vadmin\system\crud.py 行129
tab_id=("in", ["1", "9"]) => tab_id=("in", [1, 9])


PQ的数据类型输入比较严格, 布尔类型不能用1/0, 日期要用datetime对象, 是咋样就咋样..

参考:

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bennybi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值