第130集_数据库综合项目
1. 项目概述
1.1 项目目标
本集将创建一个学生管理系统,作为数据库知识的综合应用项目。通过这个项目,我们将学习如何:
- 设计合理的数据库结构
- 使用SQLAlchemy进行数据库操作
- 实现完整的CRUD功能
- 处理数据关系和事务
- 编写可维护的数据库应用
1.2 技术栈
- 后端框架:Python
- ORM工具:SQLAlchemy
- 数据库:SQLite(开发环境)/ MySQL/PostgreSQL(生产环境)
- 项目结构:模块化设计,清晰的代码分层
2. 数据库设计
2.1 需求分析
学生管理系统需要包含以下核心功能:
- 学生信息管理(增删改查)
- 课程信息管理
- 成绩管理
- 教师信息管理
- 班级信息管理
2.2 ER图设计
+-----------+ +-----------+ +-----------+
| Student | | Course | | Teacher |
+-----------+ +-----------+ +-----------+
| id | | id | | id |
| name | | name | | name |
| gender | | code | | email |
| birthday | | credit | | phone |
| class_id |<------>| | | |
+-----------+ +-----------+ +-----------+
| ^ ^
| | |
v | |
+-----------+ +------------------+ |
| Class | | Score | |
+-----------+ +------------------+ |
| id | | id | |
| name | | student_id | |
| teacher_id|<-------| course_id |-------+
+-----------+ | score |
| exam_date |
+------------------+2.3 表结构设计
1. students表
| 字段名 | 数据类型 | 约束 | 描述 |
|---|---|---|---|
| id | INTEGER | PRIMARY KEY | 学生ID |
| name | VARCHAR(50) | NOT NULL | 学生姓名 |
| gender | VARCHAR(10) | NOT NULL | 性别 |
| birthday | DATE | 出生日期 | |
| class_id | INTEGER | FOREIGN KEY | 所属班级ID |
2. classes表
| 字段名 | 数据类型 | 约束 | 描述 |
|---|---|---|---|
| id | INTEGER | PRIMARY KEY | 班级ID |
| name | VARCHAR(50) | NOT NULL | 班级名称 |
| teacher_id | INTEGER | FOREIGN KEY | 班主任ID |
3. teachers表
| 字段名 | 数据类型 | 约束 | 描述 |
|---|---|---|---|
| id | INTEGER | PRIMARY KEY | 教师ID |
| name | VARCHAR(50) | NOT NULL | 教师姓名 |
| VARCHAR(100) | UNIQUE | 邮箱 | |
| phone | VARCHAR(20) | 电话 |
4. courses表
| 字段名 | 数据类型 | 约束 | 描述 |
|---|---|---|---|
| id | INTEGER | PRIMARY KEY | 课程ID |
| name | VARCHAR(100) | NOT NULL | 课程名称 |
| code | VARCHAR(20) | UNIQUE | 课程代码 |
| credit | INTEGER | NOT NULL | 学分 |
5. scores表
| 字段名 | 数据类型 | 约束 | 描述 |
|---|---|---|---|
| id | INTEGER | PRIMARY KEY | 成绩ID |
| student_id | INTEGER | FOREIGN KEY | 学生ID |
| course_id | INTEGER | FOREIGN KEY | 课程ID |
| score | FLOAT | 分数 | |
| exam_date | DATE | NOT NULL | 考试日期 |
3. 项目架构设计
3.1 目录结构
student_management_system/
├── app/
│ ├── __init__.py # 应用初始化
│ ├── models/ # 数据模型
│ │ ├── __init__.py
│ │ ├── student.py # 学生模型
│ │ ├── class_.py # 班级模型
│ │ ├── teacher.py # 教师模型
│ │ ├── course.py # 课程模型
│ │ └── score.py # 成绩模型
│ ├── services/ # 业务逻辑
│ │ ├── __init__.py
│ │ ├── student_service.py
│ │ ├── class_service.py
│ │ ├── teacher_service.py
│ │ ├── course_service.py
│ │ └── score_service.py
│ ├── utils/ # 工具函数
│ │ ├── __init__.py
│ │ └── db.py # 数据库连接
│ └── main.py # 主程序入口
├── config.py # 配置文件
├── requirements.txt # 依赖列表
└── README.md # 项目说明3.2 模块划分
- models层:定义数据库模型和关系
- services层:实现业务逻辑和CRUD操作
- utils层:提供数据库连接、配置管理等工具
- main层:程序入口,提供用户交互界面
4. 核心功能实现
4.1 数据库连接配置
# app/utils/db.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
import config
# 创建数据库引擎
engine = create_engine(config.DATABASE_URL)
# 创建会话工厂
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# 创建基类
Base = declarative_base()
# 获取数据库会话
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()4.2 数据模型定义
学生模型
# app/models/student.py
from sqlalchemy import Column, Integer, String, Date, ForeignKey
from sqlalchemy.orm import relationship
from app.utils.db import Base
class Student(Base):
__tablename__ = "students"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(50), nullable=False)
gender = Column(String(10), nullable=False)
birthday = Column(Date)
class_id = Column(Integer, ForeignKey("classes.id"))
# 关系
class_ = relationship("Class", back_populates="students")
scores = relationship("Score", back_populates="student")班级模型
# app/models/class_.py
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from app.utils.db import Base
class Class(Base):
__tablename__ = "classes"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(50), nullable=False)
teacher_id = Column(Integer, ForeignKey("teachers.id"))
# 关系
teacher = relationship("Teacher", back_populates="classes")
students = relationship("Student", back_populates="class_")4.3 业务逻辑实现
学生服务
# app/services/student_service.py
from sqlalchemy.orm import Session
from app.models.student import Student
from app.models.class_ import Class
class StudentService:
@staticmethod
def create_student(db: Session, name: str, gender: str, birthday: str, class_id: int):
"""创建学生"""
db_student = Student(name=name, gender=gender, birthday=birthday, class_id=class_id)
db.add(db_student)
db.commit()
db.refresh(db_student)
return db_student
@staticmethod
def get_student(db: Session, student_id: int):
"""获取学生信息"""
return db.query(Student).filter(Student.id == student_id).first()
@staticmethod
def get_students(db: Session, skip: int = 0, limit: int = 100):
"""获取学生列表"""
return db.query(Student).offset(skip).limit(limit).all()
@staticmethod
def update_student(db: Session, student_id: int, **kwargs):
"""更新学生信息"""
db_student = db.query(Student).filter(Student.id == student_id).first()
if db_student:
for key, value in kwargs.items():
setattr(db_student, key, value)
db.commit()
db.refresh(db_student)
return db_student
@staticmethod
def delete_student(db: Session, student_id: int):
"""删除学生"""
db_student = db.query(Student).filter(Student.id == student_id).first()
if db_student:
db.delete(db_student)
db.commit()
return db_student
@staticmethod
def get_students_by_class(db: Session, class_id: int):
"""获取班级所有学生"""
return db.query(Student).filter(Student.class_id == class_id).all()4.4 主程序实现
# app/main.py
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from app.utils.db import get_db, engine, Base
from app.services import student_service, class_service, teacher_service, course_service, score_service
from app.models import student, class_, teacher, course, score
# 创建数据库表
Base.metadata.create_all(bind=engine)
# 创建FastAPI应用
app = FastAPI(title="学生管理系统", description="一个基于FastAPI和SQLAlchemy的学生管理系统")
# 学生相关接口
@app.post("/students/")
def create_student(name: str, gender: str, birthday: str, class_id: int, db: Session = Depends(get_db)):
return student_service.StudentService.create_student(db, name, gender, birthday, class_id)
@app.get("/students/{student_id}")
def get_student(student_id: int, db: Session = Depends(get_db)):
student = student_service.StudentService.get_student(db, student_id)
if not student:
raise HTTPException(status_code=404, detail="学生不存在")
return student
@app.get("/students/")
def get_students(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
return student_service.StudentService.get_students(db, skip, limit)
# 其他接口省略...5. 项目运行与测试
5.1 安装依赖
pip install -r requirements.txt5.2 配置文件
# config.py
# 数据库连接配置
DATABASE_URL = "sqlite:///./student_management.db"
# 生产环境可以使用MySQL或PostgreSQL
# DATABASE_URL = "mysql+pymysql://root:password@localhost/student_management"
# DATABASE_URL = "postgresql+psycopg2://user:password@localhost/student_management"5.3 启动项目
uvicorn app.main:app --reload5.4 API测试
使用FastAPI自动生成的文档测试API:
- 访问:http://localhost:8000/docs
- 可以测试所有的CRUD接口
6. 高级功能与优化
6.1 数据验证
使用Pydantic进行数据验证:
# app/schemas/student.py
from pydantic import BaseModel, Field
from datetime import date
class StudentBase(BaseModel):
name: str = Field(..., min_length=1, max_length=50)
gender: str = Field(..., pattern="^(男|女)$")
birthday: date
class_id: int
class StudentCreate(StudentBase):
pass
class StudentUpdate(StudentBase):
pass
class StudentInDB(StudentBase):
id: int
class Config:
orm_mode = True6.2 分页功能
# app/services/student_service.py
@staticmethod
def get_students(db: Session, skip: int = 0, limit: int = 10):
"""获取分页学生列表"""
total = db.query(Student).count()
students = db.query(Student).offset(skip).limit(limit).all()
return {
"total": total,
"skip": skip,
"limit": limit,
"students": students
}6.3 事务处理
# app/services/score_service.py
@staticmethod
def add_score_with_transaction(db: Session, student_id: int, course_id: int, score_value: float):
"""使用事务添加成绩"""
try:
# 开启事务
with db.begin():
# 检查学生和课程是否存在
student = db.query(Student).filter(Student.id == student_id).first()
course = db.query(Course).filter(Course.id == course_id).first()
if not student or not course:
raise ValueError("学生或课程不存在")
# 添加成绩
new_score = Score(
student_id=student_id,
course_id=course_id,
score=score_value,
exam_date=date.today()
)
db.add(new_score)
return new_score
except Exception as e:
# 事务会自动回滚
raise e7. 总结与扩展
7.1 项目总结
通过这个学生管理系统项目,我们学习了:
- 如何设计合理的数据库结构
- 如何使用SQLAlchemy进行ORM操作
- 如何实现完整的CRUD功能
- 如何处理数据关系和事务
- 如何构建一个模块化的Python应用
7.2 扩展功能
可以进一步扩展的功能:
- 用户认证和授权
- 数据导入导出(Excel、CSV)
- 数据统计和报表生成
- 邮件通知功能
- 前端界面开发(使用Vue.js或React)
7.3 最佳实践
- 数据库设计:遵循范式原则,避免数据冗余
- 代码分层:清晰的模块划分,降低耦合度
- 错误处理:完善的异常处理机制
- 数据验证:严格的数据输入验证
- 事务管理:确保数据一致性
- 测试覆盖:编写单元测试和集成测试
8. 下一步学习
完成这个综合项目后,我们将进入Web开发阶段,学习如何:
- 使用Flask/FastAPI构建Web应用
- 实现前后端分离架构
- 部署和维护Web应用
数据库是Web开发的基础,掌握好数据库知识将为后续的Web开发学习打下坚实的基础。