作为一名长期从事Python开发的程序员,我最近完成了一个图书管理系统的开发项目。这个系统最初是为某高校图书馆设计的,目的是解决传统手工管理图书效率低下、容易出错的问题。经过多次迭代优化,现在已经形成了一套功能完善、易于部署的解决方案。
图书管理系统本质上是一个信息管理系统(MIS),它通过计算机技术对图书信息进行采集、存储、处理、传输和管理。相比传统的手工管理方式,这种电子化管理具有以下显著优势:
这个系统特别适合以下几种场景:
本系统采用经典的三层架构设计:
这种分层架构的优势在于:
选择Python作为后端开发语言主要基于以下考虑:
Flask框架的选择理由:
虽然这是一个主要面向管理员的系统,但我们仍然注重用户体验:
SQLite的适用场景分析:
对于更大规模的应用,可以考虑迁移到MySQL或PostgreSQL。
sql复制CREATE TABLE users (
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
user_name TEXT NOT NULL UNIQUE,
pwd TEXT NOT NULL,
college TEXT,
num TEXT,
email TEXT,
role TEXT DEFAULT 'reader' CHECK(role IN ('admin', 'reader'))
);
设计要点:
sql复制CREATE TABLE books (
book_id TEXT PRIMARY KEY,
book_name TEXT NOT NULL,
author TEXT NOT NULL,
publish_com TEXT NOT NULL,
publish_date TEXT NOT NULL,
category TEXT,
location TEXT,
status TEXT DEFAULT 'available' CHECK(status IN ('available', 'borrowed'))
);
优化考虑:
sql复制CREATE TABLE borrows (
user_name TEXT NOT NULL,
book_id TEXT NOT NULL,
date_borrow TEXT NOT NULL,
date_return TEXT NOT NULL,
PRIMARY KEY (user_name, book_id),
FOREIGN KEY (user_name) REFERENCES users(user_name),
FOREIGN KEY (book_id) REFERENCES books(book_id)
);
关键设计:
为了提高代码复用性和安全性,我们封装了数据库操作类:
python复制class Database:
def __init__(self, db_path):
self.db_path = db_path
self.conn = None
def __enter__(self):
self.conn = sqlite3.connect(self.db_path)
self.conn.row_factory = sqlite3.Row
return self.conn.cursor()
def __exit__(self, exc_type, exc_val, exc_tb):
if self.conn:
if exc_type is None:
self.conn.commit()
else:
self.conn.rollback()
self.conn.close()
使用示例:
python复制with Database('book.db') as cursor:
cursor.execute("SELECT * FROM books WHERE status=?", ('available',))
available_books = cursor.fetchall()
python复制import hashlib
import os
def generate_salt():
return os.urandom(16).hex()
def hash_password(password, salt):
return hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt.encode('utf-8'),
100000
).hex()
# 使用示例
salt = generate_salt()
hashed_pwd = hash_password('user123', salt)
安全注意事项:
python复制@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
with Database('book.db') as cursor:
cursor.execute(
"SELECT user_id, pwd, salt FROM users WHERE user_name=?",
(username,)
)
user = cursor.fetchone()
if user and hash_password(password, user['salt']) == user['pwd']:
session['user_id'] = user['user_id']
return redirect(url_for('dashboard'))
flash('Invalid username or password')
return render_template('login.html')
python复制@app.route('/books/add', methods=['GET', 'POST'])
@login_required
@admin_required
def add_book():
if request.method == 'POST':
book_data = {
'book_id': request.form['isbn'],
'book_name': request.form['title'],
'author': request.form['author'],
'publish_com': request.form['publisher'],
'publish_date': request.form['publish_date'],
'category': request.form['category'],
'location': request.form['location']
}
try:
with Database('book.db') as cursor:
cursor.execute("""
INSERT INTO books
(book_id, book_name, author, publish_com, publish_date, category, location)
VALUES (:book_id, :book_name, :author, :publish_com, :publish_date, :category, :location)
""", book_data)
flash('Book added successfully')
return redirect(url_for('book_list'))
except sqlite3.IntegrityError:
flash('Book with this ISBN already exists')
return render_template('add_book.html')
实现多条件组合查询:
python复制@app.route('/books')
@login_required
def book_list():
query = "SELECT * FROM books WHERE 1=1"
params = []
if 'title' in request.args:
query += " AND book_name LIKE ?"
params.append(f"%{request.args['title']}%")
if 'author' in request.args:
query += " AND author LIKE ?"
params.append(f"%{request.args['author']}%")
if 'category' in request.args:
query += " AND category = ?"
params.append(request.args['category'])
with Database('book.db') as cursor:
cursor.execute(query, params)
books = cursor.fetchall()
return render_template('book_list.html', books=books)
python复制@app.route('/books/<book_id>/borrow', methods=['POST'])
@login_required
def borrow_book(book_id):
# 检查用户是否已达到借阅上限
with Database('book.db') as cursor:
cursor.execute("""
SELECT COUNT(*) as count
FROM borrows
WHERE user_name = ?
""", (session['user_name'],))
borrow_count = cursor.fetchone()['count']
if borrow_count >= 3:
flash('You have reached the maximum borrowing limit (3 books)')
return redirect(url_for('book_detail', book_id=book_id))
# 检查图书是否可借
cursor.execute("""
SELECT status FROM books WHERE book_id = ?
""", (book_id,))
book_status = cursor.fetchone()['status']
if book_status != 'available':
flash('This book is not available for borrowing')
return redirect(url_for('book_detail', book_id=book_id))
# 执行借阅操作
borrow_date = datetime.now().strftime('%Y-%m-%d')
return_date = (datetime.now() + timedelta(days=30)).strftime('%Y-%m-%d')
try:
cursor.execute("""
UPDATE books SET status = 'borrowed' WHERE book_id = ?
""", (book_id,))
cursor.execute("""
INSERT INTO borrows
(user_name, book_id, date_borrow, date_return)
VALUES (?, ?, ?, ?)
""", (session['user_name'], book_id, borrow_date, return_date))
cursor.execute("""
INSERT INTO histories
(user_name, book_id, date_borrow, status)
VALUES (?, ?, ?, 'borrowed')
""", (session['user_name'], book_id, borrow_date))
flash('Book borrowed successfully')
except sqlite3.Error as e:
flash('Error occurred while processing your request')
return redirect(url_for('book_detail', book_id=book_id))
python复制@app.route('/books/<book_id>/return', methods=['POST'])
@login_required
def return_book(book_id):
return_date = datetime.now().strftime('%Y-%m-%d')
with Database('book.db') as cursor:
try:
# 更新图书状态
cursor.execute("""
UPDATE books SET status = 'available' WHERE book_id = ?
""", (book_id,))
# 删除借阅记录
cursor.execute("""
DELETE FROM borrows
WHERE user_name = ? AND book_id = ?
""", (session['user_name'], book_id))
# 更新借阅历史
cursor.execute("""
UPDATE histories
SET date_return = ?, status = 'returned'
WHERE user_name = ? AND book_id = ? AND status = 'borrowed'
""", (return_date, session['user_name'], book_id))
flash('Book returned successfully')
except sqlite3.Error as e:
flash('Error occurred while processing your request')
return redirect(url_for('book_detail', book_id=book_id))
bash复制# 创建虚拟环境
python -m venv venv
# 激活虚拟环境
# Windows:
venv\Scripts\activate
# Linux/Mac:
source venv/bin/activate
# 安装依赖
pip install flask sqlite3
python复制from book import init_db
init_db()
对于正式环境部署,建议:
bash复制pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:8000 book:app
nginx复制server {
listen 80;
server_name library.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
问题现象:
频繁出现"database is locked"错误
解决方案:
python复制app.config['SQLITE_TIMEOUT'] = 10
问题场景:
多个用户同时借阅同一本书
解决方案:
使用数据库事务和乐观锁:
python复制with Database('book.db') as cursor:
cursor.execute("BEGIN IMMEDIATE")
try:
# 检查图书状态
cursor.execute("SELECT status FROM books WHERE book_id=? FOR UPDATE", (book_id,))
status = cursor.fetchone()['status']
if status == 'available':
# 执行借阅操作
pass
except:
cursor.execute("ROLLBACK")
raise
else:
cursor.execute("COMMIT")
常见风险:
防护措施:
python复制app.config['SECRET_KEY'] = 'your-secret-key'
在实际开发过程中,我发现良好的错误处理和日志记录系统对后期维护非常重要。建议在关键业务节点添加详细的日志记录,便于问题排查。同时,编写完整的单元测试和集成测试可以显著提高代码质量。