2.1 总体架构
在这个阶段,我们将设计一个用于管理任务的应用程序架构。我们会明确前端、后端和数据库之间的交互关系,以及它们各自包含的组件。
应用程序将由三个主要部分组成:
- Frontend (ReactJS): 提供用户与系统交互的客户端部分。
- Backend (Flask): 处理来自前端的请求并与数据库交互的服务器部分。
- Database (PostgreSQL): 用于存储用户和任务数据的存储部分。
架构图如下:
Terminal
+-------------+ +-------------+ +--------------+
| | | | | |
| Frontend +------->+ Backend +------->+ Database |
| (ReactJS) | | (Flask) | | (PostgreSQL) |
| | | | | |
+-------------+ +-------------+ +--------------+
组件之间的交互
- Frontend: 发送HTTP请求到后端以执行CRUD操作(创建、读取、更新、删除任务)。
- Backend: 处理来自前端的HTTP请求,执行业务逻辑并与数据库交互。
- Database: 根据后端的请求保存和提供数据。
2.2 每个组件的描述
1. Frontend (ReactJS):
- 界面组件: 用于用户注册和登录、创建和编辑任务、查看任务列表的组件。
- 与API的交互: 使用Axios库发送HTTP请求到后台。
2. Backend (Flask):
- REST API: 实现用户和任务管理的端点。
- 数据模型: 使用SQLAlchemy定义用户和任务的数据模型。
- 业务逻辑: 处理应用程序逻辑,包括数据验证和用户会话管理。
3. Database (PostgreSQL):
- 表: 用于存储关于用户和任务信息的表。
- 表关系: 定义用户和任务表之间的关系(例如,一个用户可以有很多任务)。
4. 网络交互
所有组件之间的通信将通过HTTP协议进行。Frontend将通过REST API与Backend交互,而Backend将通过SQL查询与数据库交互。
- Frontend: 开发和测试使用端口3000。
- Backend: 用于与Frontend交互的端口5000。
- Database: 用于与Backend交互的端口5432。
2.3 每个组件的详细描述
1. 数据的基本结构
我们将在PostgreSQL数据库中创建两个表来存储用户和任务的数据:users 和 tasks。
表 users:
-
id (int, primary key): 用户的唯一标识符。 username (varchar, unique): 用户名。password (varchar): 用户密码的哈希值。
表 tasks:
-
id (int, primary key): 任务的唯一标识符。 title (varchar): 任务标题。description (text): 任务描述。-
owner_id (int, foreign key): 分配任务的用户ID。 -
status (varchar): 任务状态(例如,已完成/未完成)。
2. API设计
后端将提供RESTful API与前端交互。可能的端点列表如下:
- 用户:
- POST /users: 创建新用户。
- GET /users: 获取所有用户的列表。
- GET /users/:id: 获取特定用户的信息。
- PUT /users/:id: 更新用户信息。
- DELETE /users/:id: 删除用户。
- 任务:
- POST /tasks: 创建新任务。
- GET /tasks: 获取所有任务的列表。
- GET /tasks/:id: 获取特定任务的信息。
- PUT /tasks/:id: 更新任务信息。
- DELETE /tasks/:id: 删除任务。
2.4 数据模型
以下是使用 Python 操作数据库表的代码示例:
User 模型:
Python
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(120), nullable=False)
tasks = db.relationship('Task', backref='owner', lazy=True)
def to_dict(self):
return {
"id": self.id,
"username": self.username
}
Task 模型:
Python
from app import db
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(120), nullable=False)
description = db.Column(db.Text, nullable=True)
owner_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
status = db.Column(db.String(20), nullable=False, default="未完成")
def to_dict(self):
return {
"id": self.id,
"title": self.title,
"description": self.description,
"owner_id": self.owner_id,
"status": self.status
}
2.5 路由和控制器
在服务器端实现API的示例:
Python
from app import app, db
from app.models import Task, User
from flask import request, jsonify
@app.route('/tasks', methods=['GET'])
def get_tasks():
tasks = Task.query.all()
return jsonify([task.to_dict() for task in tasks])
@app.route('/tasks', methods=['POST'])
def create_task():
data = request.get_json()
new_task = Task(
title=data['title'],
description=data.get('description'),
owner_id=data['owner_id'],
status=data.get('status', "未完成")
)
db.session.add(new_task)
db.session.commit()
return jsonify(new_task.to_dict()), 201
@app.route('/tasks/<int:id>', methods=['GET'])
def get_task(id):
task = Task.query.get_or_404(id)
return jsonify(task.to_dict())
@app.route('/tasks/<int:id>', methods=['PUT'])
def update_task(id):
data = request.get_json()
task = Task.query.get_or_404(id)
task.title = data['title']
task.description = data.get('description')
task.status = data.get('status', task.status)
task.owner_id = data['owner_id']
db.session.commit()
return jsonify(task.to_dict())
@app.route('/tasks/<int:id>', methods=['DELETE'])
def delete_task(id):
task = Task.query.get_or_404(id)
db.session.delete(task)
db.session.commit()
return '', 204
2.6 前端请求服务器的例子
用于显示任务列表的 React 组件示例:
Javascript
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const TaskList = () => {
const [tasks, setTasks] = useState([]);
useEffect(() => {
axios.get('http://localhost:5000/tasks')
.then(response => {
setTasks(response.data);
})
.catch(error => {
console.error('获取任务时出错了!', error);
});
}, []);
return (
<div>
<h1>任务列表</h1>
<ul>
{tasks.map(task => (
<li key={task.id}>{task.title} - {task.status}</li>
))}
</ul>
</div>
);
};
export default TaskList;
GO TO FULL VERSION