概述
项目中需要快速搭建一个前后端系统,涉及到dash-fastapi架构的时候,对该架构的时候进行总结。本文主要总结的是对该架构的基本使用,后续再对该架构的项目源码进行总结分析
此处实现一个小的demo,迷你任务管理器,后端使用fastapi,前端则使用dash,数据存储暂时使用列表进行存储,主要功能如下
- 任务列表展示: 前端页面显示一个简单的任务列表,包含任务标题和状态。
- 添加任务: 用户可以在前端输入任务标题,点击按钮添加新任务。
- 刷新任务列表: 点击按钮可以刷新任务列表,从后端获取最新数据。
整体架构理解
代码主体架构
后端
- main.py (fast api主文件)
- requirements.txt(后端依赖)
前端
- app.py(dash主文件)
- api_client.py(前端api客户端)
- layoputs.py(前端布局)
- callbacks.py(前端回调函数)
- requirements.txt(后端依赖)
主要逻辑理解
代码中具体体现
后端
- main.py:后端,也就类似于厨房。专门负责接收顾客的订单,然后准备食物(构建响应)并告知服务器食物准备后
- tasks_db = []:通过列表内存列表,类似于厨师的菜单列表。其记录了餐厅可以提供的菜品,也就是后端可以完成的任务
- @app.get :厨师提供给服务员今日菜单,服务员发送get请求的时候,就可以知道后端提供什么服务(从tasks_db中获取)
- @app.post:创创建任务,类似于服务员将菜单传给厨房;后面的逻辑就是请求响应的基本逻辑
- task(使用pydantic模型):菜单上的菜品叙述,规定了每个任务包含哪些信息,提供任务名以及状态是否完成
前端
- layouts.py:餐厅的菜单,定义了顾客可以看到什么,也就是前端显示的页面
- dcc.input:点餐单的填写区域,顾客要吃什么
- dbc.button:提交按钮,这里可以对应设计供,例如提交点餐单或者是刷新菜单信息
- html.div:上菜的盘子,厨房准备后的食物会放进这个盘子里展示给顾客
- callbacks.py:服务员接收到顾客的指令应该如何行动
- api_client.py:点餐系统,帮助前端与后端沟通,服务员与厨师之间的沟通
具体实现
该实例主要用于理解该结构的运行
代码
后端:主要提供两个方法,获取所有任务列表和创建新任务
# backend/main.py from fastapi import fastapi, httpexception from pydantic import basemodel from typing import list app = fastapi() # 模拟内存数据库 (使用 python 列表) tasks_db = [] # 用于生成唯一的用户id task_id_counter = 1 class task(basemodel): id: int title: str status: str = "待完成" # 默认状态 class taskcreate(basemodel): title: str class taskresponse(basemodel): tasks: list[task] @app.get("/api/tasks", response_model=taskresponse) async def get_tasks(): """获取所有任务列表""" return taskresponse(tasks=tasks_db) @app.post("/api/tasks", response_model=task) async def create_task(task_create: taskcreate): """创建新任务""" global task_id_counter new_task = task(id=task_id_counter, title=task_create.title) tasks_db.append(new_task) task_id_counter += 1 return new_task if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000, reload=true)
后端依赖:requirements.txt
fastapi uvicorn pydantic
前端代码:api_client.py(向服务端发起请求)
import requests api_base_url = "http://localhost:8000/api" # 后端 api 基础 url def get_task_list(): """获取任务列表""" url = f"{api_base_url}/tasks" response = requests.get(url) response.raise_for_status() # 检查请求是否成功 (状态码 2xx) return response.json() def create_new_task(title): """创建新任务""" url = f"{api_base_url}/tasks" headers = {'content-type': 'application/json'} data = {'title': title} response = requests.post(url, headers=headers, json=data) response.raise_for_status() return response.json()
前端回调:callbacks.py,当顾客触碰哪些按钮后与后端交互然后返回的逻辑实现
from dash import output, input, state from .app import app # 导入 dash app 实例 from frontend import api_client # 导入 api 客户端 import dash_html_components as html import dash @app.callback( output("task-list-output", "children"), [input("refresh-tasks-button", "n_clicks"), input("add-task-button", "n_clicks")], [state("new-task-title", "value")] ) def update_task_list(refresh_clicks, add_clicks, new_task_title): """更新任务列表显示""" triggered_id = [p['prop_id'] for p in dash.callback_context.triggered][0] if "add-task-button" in triggered_id: if new_task_title: api_client.create_new_task(new_task_title) # 调用 api 创建新任务 tasks_data = api_client.get_task_list() # 调用 api 获取任务列表 task_items = [] if tasks_data and tasks_data.get('tasks'): # 检查 tasks_data 和 tasks 键是否存在 for task in tasks_data['tasks']: task_items.append(html.li(f"{task['title']} - 状态: {task['status']} (id: {task['id']})")) else: task_items.append(html.li("暂无任务")) return html.ul(task_items)
前端页面布局layouts.py
import dash_html_components as html import dash_core_components as dcc import dash_bootstrap_components as dbc layout = dbc.container([ html.h1("迷你任务管理器"), dbc.row([ dbc.col([ html.div("任务标题:"), dcc.input(id="new-task-title", type="text", placeholder="请输入任务标题"), dbc.button("添加任务", id="add-task-button", n_clicks=0, classname="mt-2"), ]), ]), html.hr(classname="mt-3"), html.h4("任务列表"), dbc.button("刷新任务列表", id="refresh-tasks-button", n_clicks=0, classname="mb-2"), html.div(id="task-list-output"), # 用于显示任务列表 ])
前端依赖
dash dash-bootstrap-components requests
pydantic补充
""" 简单事例 """ # from pydantic import basemodel # # class user(basemodel): # id: int # name: str # email: str # is_active: bool = true # 默认值 # # # 示例数据 # user_data = { # 'id': 1, # 'name': 'alice', # 'email': 'alice@example.com', # } # # # 使用 pydantic 模型进行数据验证和解析 # user = user(**user_data) # print(user) """ 复杂事例的封装 """ from pydantic import basemodel from typing import list class address(basemodel): street: str city: str zip_code: str class user(basemodel): id: int name: str address: address # 嵌套模型 # 创建嵌套数据 user_data = { 'id': 1, 'name': 'john', 'address': { 'street': '123 main st', 'city': 'new york', 'zip_code': '10001', } } user = user(**user_data) print(user)
前端回调逻辑
@app.callback( output("task-list-output", "children"), [input("refresh-tasks-button", "n_clicks"), input("add-task-button", "n_clicks")], [state("new-task-title", "value")] ) def update_task_list(refresh_clicks, add_clicks, new_task_title): """更新任务列表显示""" triggered_id = [p['prop_id'] for p in dash.callback_context.triggered][0] if "add-task-button" in triggered_id: if new_task_title: api_client.create_new_task(new_task_title) # 调用 api 创建新任务 tasks_data = api_client.get_task_list() # 调用 api 获取任务列表 task_items = [] if tasks_data and tasks_data.get('tasks'): # 检查 tasks_data 和 tasks 键是否存在 for task in tasks_data['tasks']: task_items.append(html.li(f"{task['title']} - 状态: {task['status']} (id: {task['id']})")) else: task_items.append(html.li("暂无任务")) return html.ul(task_items)
回调函数callbacks理解
dash框架中回调函数是实现交互的关键,其一可以连接前端的ui组件交互和侯丹数据的处理逻辑(调用api或者更新图表操作),从而实现动态更新前端ui,而不需要更新整个页面
output("task-list-output", "children")
(输出)
该处定义了回调函数的输出,其指定了当回调函数执行完毕后,哪个前端组件的哪个属性会被更新
html.div(id="task-list-output") # <--- 这里定义了 id="task-list-output" 的 div 组件
这个回调函数执行完成后,将会更新 id
为 "task-list-output"
的 div
组件的 children
属性。 换句话说,回调函数的返回值将会被设置为这个 div
组件的内容,从而更新任务列表的显示
换句话说,output就是上菜的盘子,盘子里面的内容就是children属性
[input("refresh-tasks-button", "n_clicks"), input("add-task-button", "n_clicks")]
(输入 - 触发器)
指定了当前前端组件的哪些属性发生变化的时候,会触发这个回调函数执行
dbc.button("刷新任务列表", id="refresh-tasks-button", ...) # <--- 这里定义了 id="refresh-tasks-button" 的按钮
类似于顾客点击菜价查询,服务员就会去问一下菜价,当顾客点击提交餐单的时候,服务员就会立马去厨房下单
[state("new-task-title", "value")]
(状态)
指定哪些前端组件的哪些属性的当前值需要传递给回调函数,但是state组件属性的变化不会触发回调函数执行
可以理解state就是顾客在菜单上书写的菜名
当 update_task_list
回调函数被触发执行时 (因为 "刷新任务列表" 按钮或 "添加任务" 按钮被点击了),dash 框架会将 id
为 "new-task-title"
的输入框组件的 value
属性的 "当前值" 作为参数传递给 update_task_list
函数。 注意,输入框内容的变化 不会 直接触发回调函数,只有当 input
指定的组件属性变化时才会触发
到此这篇关于python dash-fastapi前后端搭建的文章就介绍到这了,更多相关python dash-fastapi搭建内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论