WEB开发: 全栈工程师起步 - Python Flask +SQLite的管理系统实现
一、前言
罗马不是一天建成的。
每个全栈工程师都是从HELLO WORLD 起步的。
之前我们分别用NODE.JS 、ASP.NET Core 这两个框架实现过基于WebServer的全栈工程师入门教程。
今天我们用更简单的来实现: Python。
我们将用Python来实现一个学生管理应用,它包括Web服务器+管理前端+后端Api+数据库,并满足一个管理应用所具有的基本的增删查功能。由此来熟悉Phthon的webServer (Flask )应用。
二、流程和技术栈
先来看下我们设想的应用流程
然后根据这个来设定一下我们的技术栈:
-
前端:HTML + JS + CSS
前端开发利用HTML来构建网页的骨架和内容结构,CSS用于定义网页的样式和布局,如字体、颜色、空间等,确保网页在不同设备上的视觉效果一致。JavaScript则负责网页的动态交互功能,如响应用户输入、动画效果和与服务器的通信,使得网页变得互动性强,提升用户体验。 -
服务器:Python Flask
Flask是一个轻量级的Python Web框架,适合构建小型到中型的Web应用。它简洁、易于上手,并且高度可扩展。Flask允许开发者定义路由、处理HTTP请求、与数据库交互,并能够快速搭建RESTful API服务,广泛用于开发Web服务和微服务。Flask的灵活性使得开发者可以根据需求选择需要的功能和扩展。 -
数据库:SQLite
SQLite是一种轻量级的嵌入式关系型数据库管理系统,适用于桌面和移动设备应用。它将数据存储在一个单一的文件中,不需要独立的数据库服务器,安装和配置非常简单。SQLite支持SQL标准,可以高效地存储、查询、更新数据,适合用在低并发、数据量较小的项目中,常用于原型开发、小型应用和嵌入式系统。
三、文件结构
所以 首先你需要在自己的电脑上安装PYTHON。默认都有了。
然后需要 pip install sqlite3。 安装这个数据库引擎。
一切齐全,准备动手,动手前先确定一下这个项目的目录文件结构:
目录结构
/student-grade-management
├── app.py # Flask应用的主程序
├── students.db # SQLite数据库文件
├── /templates # 存放HTML模板文件的文件夹
| └── index.html # 主页面的HTML文件
├── /static # 存放静态资源(如CSS, JS, 图片等)的文件夹
├── style.css # 样式文件(CSS)
└── script.js # 前端脚本(JavaScript)
这个目录结构是一个典型的 Flask 项目的结构,适用于一个简单的学生成绩管理系统。下面我将逐一解释每个文件和文件夹的作用:
详细解释
1. app.py
- 作用:这是你的 Flask Web 应用的主程序。Flask 是一个轻量级的 Python Web 框架,用于快速开发 Web 应用。
- 功能:在这个文件中,我们会定义所有的路由(URLs)、视图函数(处理请求的函数)和一些与数据库交互的代码。
- 例如:
/
路由显示学生成绩列表。/add
路由处理添加学生成绩。/delete/<id>
路由处理删除学生成绩。/search
路由处理按姓名查询学生成绩。
- 该文件还负责启动 Flask Web 服务器(通常使用
app.run()
)。
- 例如:
2. students.db
- 作用:这是 SQLite 数据库文件,存储系统中的所有数据。
- 功能:SQLite 是一个轻量级的数据库,它会将数据保存在本地文件中。这个数据库文件包含一个表(比如
students
),存储学生的姓名和成绩。- 该数据库可以使用 Python 的
sqlite3
库进行操作。 - 例如,系统会在此数据库中进行以下操作:
- 插入新学生成绩。
- 查询学生成绩。
- 删除学生成绩。
请注意这个db文件最开始需要用脚本生成,脚本中设定了各个字段、表名,见后i面的文件介绍。
- 该数据库可以使用 Python 的
3. /templates
-
作用:这个文件夹存放 Flask 应用的 HTML 模板文件,Flask 会使用 Jinja2 模板引擎来渲染这些 HTML 文件。
-
功能:
- Flask 会使用模板文件来动态生成页面内容。
- 在我们的项目中,
index.html
作为主页面模板,显示学生成绩列表,并提供添加、删除和查询功能。 - Jinja2 语法使得我们可以在 HTML 中插入 Python 变量、执行条件语句、循环等操作。
- 例如,学生的姓名和成绩列表会从数据库中获取,Flask 会将这些数据传递到模板中,然后通过模板渲染显示在页面上。
例如:
<table> <thead> <tr> <th>姓名</th> <th>成绩</th> </tr> </thead> <tbody> {% for student in students %} <tr> <td>{{ student.name }}</td> <td>{{ student.grade }}</td> </tr> {% endfor %} </tbody> </table>
4. /static
-
作用:
static
文件夹用来存放 Web 应用中不会变化的静态资源文件,如 CSS、JavaScript、图片等。 -
功能:这些静态文件是客户端直接访问的文件,不需要通过 Flask 后端处理。Flask 会自动处理
static
文件夹中的文件并提供服务。style.css
:存放样式表文件(CSS),用于控制页面的外观和布局。你可以在index.html
中引用它,定义页面的字体、颜色、布局等样式。script.js
:存放 JavaScript 文件,用于处理客户端的交互逻辑。例如,处理添加、删除、查询学生成绩的事件,或者发送 AJAX 请求来与 Flask 后端进行交互。
例如:
<link rel="stylesheet" href="/static/style.css"> <script src="/static/script.js"></script>
典型流程
- 用户在浏览器中访问 Flask 应用时,Flask 会根据请求路由选择适当的视图函数。
- 视图函数可以与数据库交互,获取或修改数据。
- 数据传递到模板中,模板使用 Jinja2 引擎动态生成 HTML 页面并返回给浏览器。
- 浏览器根据返回的 HTML 内容渲染页面,并加载
static
文件夹中的 CSS 和 JavaScript 资源。
四、源码
以下是各个部分的源码:
dbMake.py (这个是一个脚本,用来生成数据库,运行这个脚本可以在根目录下生成一个数据库文件 students.db)
import sqlite3
# 连接数据库(如果数据库文件不存在,会自动创建)
conn = sqlite3.connect('students.db')
# 创建一个cursor对象
cursor = conn.cursor()
# 创建学生成绩表
cursor.execute('''
CREATE TABLE IF NOT EXISTS students (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
grade INTEGER NOT NULL
)
''')
# 提交并关闭
conn.commit()
conn.close()
app.py
from flask import Flask, render_template, request, jsonify
import sqlite3
app = Flask(__name__)
# 获取数据库连接
def get_db_connection():
conn = sqlite3.connect('students.db')
conn.row_factory = sqlite3.Row # 返回字典类型的行
return conn
# 首页,显示学生成绩列表
@app.route('/')
def index():
conn = get_db_connection()
students = conn.execute('SELECT * FROM students').fetchall()
conn.close()
return render_template('index.html', students=students)
# 添加学生成绩
@app.route('/add', methods=['POST'])
def add_student():
name = request.form['name']
grade = request.form['grade']
conn = get_db_connection()
conn.execute('INSERT INTO students (name, grade) VALUES (?, ?)', (name, grade))
conn.commit()
conn.close()
return jsonify({"status": "success"})
# 查询学生成绩
@app.route('/search', methods=['GET'])
def search_student():
name = request.args.get('name', '')
conn = get_db_connection()
students = conn.execute('SELECT * FROM students WHERE name LIKE ?', ('%' + name + '%',)).fetchall()
conn.close()
return render_template('index.html', students=students)
# 删除学生成绩
@app.route('/delete/<int:id>', methods=['GET'])
def delete_student(id):
conn = get_db_connection()
conn.execute('DELETE FROM students WHERE id = ?', (id,))
conn.commit()
conn.close()
return jsonify({"status": "success"})
if __name__ == '__main__':
app.run(debug=True)
script.js
document.getElementById('add-student-form').addEventListener('submit', function (event) {
event.preventDefault();
const name = document.getElementById('name').value;
const grade = document.getElementById('grade').value;
fetch('/add', {
method: 'POST',
body: new URLSearchParams({
'name': name,
'grade': grade
})
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
alert('学生成绩已添加!');
window.location.reload(); // 刷新页面
}
});
});
// 删除学生成绩
function deleteStudent(id) {
if (confirm('确定要删除这个成绩吗?')) {
fetch(`/delete/${id}`)
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
alert('学生成绩已删除!');
window.location.reload(); // 刷新页面
}
});
}
}
// 查询学生成绩
document.getElementById('search-form').addEventListener('submit', function (event) {
event.preventDefault();
const name = document.getElementById('search-name').value;
fetch(`/search?name=${name}`)
.then(response => response.text())
.then(data => {
document.body.innerHTML = data; // 更新页面内容
});
});
style.css
body {
font-family: Arial, sans-serif;
margin: 20px;
background-color: #f9f9f9;
}
h1 {
text-align: center;
}
table {
width: 50%;
margin: 0 auto;
border-collapse: collapse;
}
th, td {
padding: 10px;
text-align: center;
}
form {
margin-top: 20px;
text-align: center;
}
input, button {
padding: 10px;
margin: 5px;
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>学生成绩管理系统</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<h1>学生成绩管理系统</h1>
<h2>查询学生成绩</h2>
<form id="search-form">
<label for="search-name">姓名: </label><br>
<input type="text" id="search-name" name="name"><br><br>
<button type="submit">查询</button>
</form>
<h2>学生成绩列表</h2>
<table border="1">
<thead>
<tr>
<th>姓名</th>
<th>成绩</th>
<th>操作</th>
</tr>
</thead>
<tbody id="students-list">
{% for student in students %}
<tr>
<td>{{ student.name }}</td>
<td>{{ student.grade }}</td>
<td>
<button onclick="deleteStudent({{ student.id }})">删除</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<h2>添加学生成绩</h2>
<form id="add-student-form">
<label for="name">姓名: </label><br>
<input type="text" id="name" name="name"><br><br>
<label for="grade">成绩: </label><br>
<input type="number" id="grade" name="grade"><br><br>
<button type="submit">添加</button>
</form>
<script src="/static/script.js"></script>
</body>
</html>
准备完毕,命令行切入根目录,执行 python app.py 启动应用
显示 服务器启动了 端口是5000 使用 http://127.0.0.1:5000 可以访问这个应用:
万丈高楼平地起,这个还有一些bug,可以自己修复。祝你好运!