数据库代码:
CREATE DATABASE tushuguanlixitong1;
use tushuguanlixitong1;
CREATE TABLE books (
book_id INT PRIMARY KEY AUTO_INCREMENT, -- 图书编号(主键,自增)
book_name VARCHAR(255) NOT NULL, -- 图书名
book_status VARCHAR(50) NOT NULL, -- 图书状态(如:在架、出借)
book_ISBN VARCHAR(20) UNIQUE NOT NULL, -- ISBN
author VARCHAR(255), -- 作者
press VARCHAR(255), -- 出版社
borrower VARCHAR(255), -- 借阅者姓名(如果有的话)
loan_time DATE -- 借出时间
);
CREATE TABLE reader (
reader_id INT PRIMARY KEY AUTO_INCREMENT, -- 读者编号(主键,自增)
read_name VARCHAR(255) NOT NULL, -- 读者姓名
read_identity VARCHAR(100), -- 读者身份(如:学生、教师)
read_department VARCHAR(255) -- 读者所在部门
);
CREATE TABLE borrow_records (
record_id INT PRIMARY KEY AUTO_INCREMENT, -- 借阅记录编号(主键,自增)
book_id INT, -- 图书编号(外键,关联到 books 表)
borrower VARCHAR(255) NOT NULL, -- 借阅者姓名
borrow_date DATE NOT NULL, -- 借出日期
return_date DATE, -- 归还日期(如果归还的话)
FOREIGN KEY (book_id) REFERENCES books(book_id) -- 外键约束
);
INSERT INTO books (book_name, book_status, book_ISBN, author, press)
VALUES
('Python编程', '在架', '978-7-111-60680-2', '张三', '人民邮电出版社'),
('数据结构与算法', '在架', '978-7-111-62057-1', '李四', '高等教育出版社'),
('数据库系统概论', '在架', '978-7-111-48459-9', '王五', '清华大学出版社');
INSERT INTO reader (read_name, read_identity, read_department)
VALUES
('王五', '学生', '计算机科学与技术'),
('李四', '教师', '软件工程'),
('张三', '学生', '数据科学');
INSERT INTO borrow_records (book_id, borrower, borrow_date, return_date)
VALUES
(1, '王五', '2024-12-01', NULL), -- 王五借了《Python编程》,未归还
(2, '李四', '2024-12-02', '2024-12-10'), -- 李四借了《数据结构与算法》,已归还
(3, '张三', '2024-12-05', NULL); -- 张三借了《数据库系统概论》,未归还
Python代码(但是感觉还有好多漏洞 还有很多需要完善的地方 可能还有些隐藏逻辑错误没发现 以后有待指正):
import pymysql
import datetime
connection = pymysql.connect(
host='localhost',
port=3306,
user='root',
passwd='shurumima',
db='tushuguanlixitong1',
charset='utf8'
)
def query(sql, one = False): #执行 SQL 查询并返回查询结果 sql:一个字符串,包含要执行的 SQL 查询语句。
cursor = connection.cursor() #创建一个游标对象,cursor 用于执行 SQL 查询并处理结果。游标是 Python 与数据库之间的桥梁,通过它可以执行查询、更新操作等。
cursor.execute(sql) #执行传入的 SQL 查询语句 sql
if one:
return cursor.fetchone() #返回查询结果集中的第一条记录,返回类型是元组(tuple),包含一行数据。如果没有更多数据,它返回 None
else:
return cursor.fetchall() #返回查询结果集中的所有记录,返回类型是一个列表,每一项是一个元组,表示每一行数据。如果查询结果为空,则返回一个空列表 []。
def update(sql): #执行数据库中的修改操作,比如 INSERT、UPDATE 或 DELETE 等操作
cursor = connection.cursor()
result = cursor.execute(sql) #result 保存了 cursor.execute(sql) 执行后的返回值,通常是影响的行数(即数据库中受该操作影响的记录数量)。对于 UPDATE 和 DELETE 操作,result 返回被修改或删除的行数。对于 INSERT 操作,result 返回插入的记录数。
connection.commit() #提交事务,将之前执行的 SQL 操作(比如 UPDATE、DELETE、INSERT)的修改永久保存到数据库中。
return result
def select_book():
cursor = connection.cursor()
#定义 SQL 查询语句,从图书信息表表中选择所有数据
sql = 'select * from books;'
cursor.execute(sql)
connection.commit() #对于查询操作来说这行代码并不必要(因为查询操作通常不需要提交),但如果做了数据修改(例如INSERT、UPDATE、DELETE),这行代码是必须的。
#从游标中获取查询结果,保存到 data 变量中
data = cursor.fetchall()
from pandas import DataFrame
#这些列名应该和数据库表中的列名称一致。
df = DataFrame(data, columns=['book_name', 'book_id', 'book_status', 'book_ISBN', 'author', 'press', 'borrower',
'loan_time'])
df.head() #返回 DataFrame 的前五行数据。head() 是 pandas 提供的一个方法,用于查看数据框的前几行(默认为前5行)。
print(df.head())
class Book:
def __init__(self, book_name, book_id, book_status, book_isbn, author, press):
self.book_name = book_name
self.book_id = book_id
self.book_status = book_status
self.book_ISBN = book_isbn
self.author = author
self.press = press
def add_book():
cursor = connection.cursor()
sql = 'insert into books(book_name,book_id,book_status,book_ISBN,author,press) values(%s,%s,%s,%s,%s,%s)'
print('请输入添加图书信息:')
book = Book(None, None, None, None, None, None)
book.book_name = input('请输入图书名:')
book.book_id = input('请输入图书编号:')
book.book_status = input('请输入图书状态:')
book.book_ISBN = input('请输入图书ISBN码:')
book.author = input('请输入图书作者:')
book.press = input('请输入图书出版社:')
values = (book.book_name, book.book_id, book.book_status, book.book_ISBN, book.author, book.press)
cursor.execute(sql, values)
connection.commit()
print('图书添加成功')
def delete_book():
connection.cursor()
book_id = input('输入需要删除的图书号:')
# query 是自定义函数,见上面
result = query('select * from books where book_id = {}'.format(book_id), one=True)
if result:
print('图书信息:', result)
choice = input('是否删除? 1.yes,2.no')
if choice == '1':
update('delete from books where book_id = {}'.format(book_id))
print('成功删除')
else:
print('放弃删除')
else:
print('未查询到相关书籍信息')
num = input('继续删除请输入1, 回车退回主菜单')
if num == '1':
delete_book()
def update_book():
connection.cursor()
book_id = input('输入需要更新的图书号:')
result = query('select * from books where book_id = {}'.format(book_id), one=True)
if result:
print('图书信息:', result)
book_name = input('请输入修改书名:')
book_status = input('请输入图书状态:')
author = input('请输入修改作者:')
press = input('请输入修改出版社:')
update("update books set book_name = '{}',book_status = '{}',author = '{}',press = '{}' where book_id = {};".format(book_name, book_status, author, press, book_id))
print('更新成功')
else:
print('未查询到相关书籍信息')
num = input('继续修改请输入1, 回车退回主菜单')
if num == '1':
update_book()
def select_reader():
cursor = connection.cursor()
sql = 'select * from reader;'
cursor.execute(sql)
connection.commit()
data = cursor.fetchall()
from pandas import DataFrame
df = DataFrame(data, columns=['姓名', '编号', '身份', '部门'])
df.head()
print(df.head())
class Read:
def __init__(self, read_name, read_id, read_identity, read_department):
self.read_name = read_name
self.read_id = read_id
self.read_identity = read_identity
self.read_department = read_department
def add_reader():
cursor = connection.cursor()
sql = 'insert into reader(read_name,read_id,read_identity,read_department) values(%s,%s,%s,%s)'
print('添加人员:')
read = Read(None, None, None, None)
read.read_name = input('请输入读者姓名')
read.read_id = input('请输入读者编号')
read.read_identity = input('请输入读者身份')
read.read_department = input('请输入读者所在部门')
values = (read.read_name, read.read_id, read.read_identity, read.read_department)
cursor.execute(sql, values)
connection.commit()
print('读者添加成功!')
def borrow_book():
cursor = connection.cursor()
book_id = input('请输入需要借阅的图书号:')
result = query('select * from books where book_id={};'.format(book_id), one=True)
if result:
if result[2] == '出借':
print('抱歉,该书已经借出!')
else:
borrower = input('请输入借阅者的名字:')
borrow_date = datetime.datetime.now().strftime('%Y-%m-%d')
return_date = None
sql = 'insert into borrow_records (book_id, borrower, borrow_date, return_date) values (%s, %s, %s, %s)'
values = (book_id, borrower, borrow_date, return_date)
cursor.execute(sql, values)
connection.commit()
update("update books set book_status='出借' where book_id={};".format(book_id))
print('图书借阅成功')
else:
print('未查询到相关书籍信息')
num = input('继续借书请输入1, 回车退回主菜单')
if num == '1':
borrow_book()
def back_book():
cursor = connection.cursor()
book_id = input('请输入要归还的图书书号:')
result = query('select * from books where book_id={};'.format(book_id), one=True)
if result:
if result[2] == '在架':
print('抱歉,该书在架,请确认编号是否正确!')
else:
borrow_record = query('select * from borrow_records where book_id={} and return_date IS NULL order by borrow_date desc limit 1'.format(book_id), one=True)
#使用 query() 函数查询 borrow_records 表,找到该图书的借阅记录。要求 book_id 匹配并且 return_date 为 NULL(表示该书尚未归还)。order by borrow_date desc 确保查询返回的是最新的借阅记录,limit 1 限制查询返回一条记录。
if borrow_record: #如果查询到借阅记录(即图书有借阅且未归还),则执行图书归还操作。
return_date = datetime.datetime.now().strftime('%Y-%m-%d')
cursor.execute('update borrow_records set return_date=%s where record_id=%s', (return_date, borrow_record[0])) #record_id 是借阅记录的 ID
connection.commit()
update("update books set book_status='在架' where book_id={};".format(book_id))
print('归还成功')
else:
print('未找到该书的借阅记录')
else:
print('未查询到相关书籍信息')
num = input('继续还书请输入1,回车退回主菜单')
if num == '1':
back_book()
def time():
cursor = connection.cursor()
now = datetime.datetime.now()
# 查询超期图书(归还时间小于当前时间且未归还)
cursor.execute("""
select b.book_name, br.borrower, br.borrow_date, br.return_date
from borrow_records br
join books b on br.book_id = b.book_id
where br.return_date < %s and br.return_date IS NOT NULL
""", (now,))
overdue_books = cursor.fetchall()
if overdue_books:
print('超期图书:')
for book in overdue_books:
print(f"书名: {book[0]}, 借阅者: {book[1]}, 借阅时间: {book[2]}, 归还时间: {book[3]}")
else:
print('没有超期的图书。')
# 查询临期图书(归还时间在10天内)
cursor.execute("""
select b.book_name, br.borrower, br.borrow_date, br.return_date
from borrow_records br
join books b on br.book_id = b.book_id
where br.return_date between %s and %s
""", (now, now + datetime.timedelta(days=10)))
due_soon_books = cursor.fetchall()
if due_soon_books:
print('临期图书:')
for book in due_soon_books:
print(f"书名: {book[0]}, 借阅者: {book[1]}, 借阅时间: {book[2]}, 归还时间: {book[3]}")
else:
print('没有临期的图书。')
def menu():
while True:
print("""
图书管理系统
1.查询图书
2.增加图书
3.借阅图书
4.归还图书
5.修改图书
6.删除图书
7.导入读者名单
8.查看读者名单
9.超期和临期查询
10.退出系统
""")
choice = input('请选择:')
if choice == '1':
select_book()
elif choice == '2':
add_book()
elif choice == '3':
borrow_book()
elif choice == '4':
back_book()
elif choice == '5':
update_book()
elif choice == '6':
delete_book()
elif choice == '7':
add_reader()
elif choice == '8':
select_reader()
elif choice == '9':
time()
elif choice == '10':
print('欢迎下次使用')
break
else:
print('请输入正确序号')
menu()
在delete_book()函数中,
①参考的代码中写的错误代码:
print("图书信息:".format(result))
正确:
print("图书信息:", result)
print(f"图书信息:{result}")
print("图书信息:{}".format(result))
②注意:在将 book_id 插入 SQL 查询时,使用 .format() 的方式可能会引发 SQL 注入攻击。
③注意:在 delete_book() 中使用递归调用(delete_book())来实现删除图书的功能。如果用户选择继续删除,会导致递归调用,但如果删除的操作很多,可能会引起栈溢出问题。可以考虑使用循环代替递归。
在borrow_book()函数中,
①datetime.datetime.now()
datetime 是 Python 标准库中的一个模块,用于处理日期和时间。
datetime.datetime 是 datetime 模块中的一个类,表示日期和时间。
now() 是 datetime 类的一个方法,用于获取当前的日期和时间,返回一个 datetime 对象。
strftime('%Y-%m-%d')
strftime 是 datetime 类中的一个方法,用于将 datetime 对象格式化为字符串。
strftime 方法接受一个格式字符串作为参数,表示你希望日期和时间如何被表示成字符串。
在这个例子中,格式字符串是 '%Y-%m-%d',它指定了日期的输出格式:
'%Y' 表示四位的年份(例如,2024)。
'%m' 表示两位的月份(例如,12)。
'%d' 表示两位的日期(例如,17)。
②
cursor.execute(query, parameters)
query:一个字符串,表示要执行的 SQL 查询或命令。这个字符串可以是 SELECT 查询、INSERT 语句、UPDATE 语句、DELETE 语句等。
parameters(可选):一个元组或字典,包含传递给 SQL 语句的参数。这些参数通常用于防止 SQL 注入攻击,确保数据库操作的安全性。
对于 MySQL 和 PostgreSQL,占位符通常是 %s(无论传递的参数类型是什么)