Python-Flask框架之——图书管理系统 , 附详解源码和效果图 !
该图书管理系统要实现的功能 :
1. 可以通过添加窗口添加书籍或作者, 如果要添加的作者和书籍已存在于书架上, 则给出相应的提示.
2. 如果要添加的作者存在, 而要添加的书籍书架上没有, 则将该书籍添加到该作者栏.
3. 如果要添加的作者和书籍都不存在于书架上 , 则将书籍和作者一起添加.
4. 每个书籍和作者旁边都有一个删除按钮 , 点击删除书籍的按钮可以将该书籍删除 , 若某作者栏的书籍全部删除完毕则显示"无".
5. 若直接点击删除作者按钮, 则可以将该作者和其书籍一起全部删掉.
该系统的实现工具: Python的Flask框架和MySQL数据库.
效果图及源码如下:



Python源代码如下:
# coding=utf-8
from flask import Flask,render_template,request,flash,redirect,url_for
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import StringField,SubmitField
from wtforms.validators import DataRequired app = Flask(__name__)
"""
1. 配置数据库
a.导入SQLALchemy扩展
b.创建db对象, 并配置参数
c.终端创建数据库
2. 添加作者和书模型(类)
a.模型继承自db.Model
b.__tablename__:表名
c. db.Column:字段
d. db.relationship:关系引用
3. 添加数据
4. 使用模板显示数据库查询到的数据
a.查询所有的作者信息, 让信息传递给模板
b.模板中按照格式, 依次for循环作者和书籍即可(通过作者获取书籍, 用的是关系引用)
5. 使用WTF显示表单
a.自定义表单类
b.模板中显示
c.设置secret_key
6. 实现相关的增删逻辑
a.添加作者/书籍
b.删除书籍: redirect(重定向)/url_for(指向路由)/for else 的使用.
c.删除作者(要先删除该作者的书籍, 再删除该作者)
"""
# 配置数据库的地址URI , 格式 "数据库类型+数据库驱动名称://用户名:密码@机器地址:端口号/数据库名" , 端口号可以不写.
# python3中用的mysql驱动是mysql-connector , 已经不支持python2的MySQLdb驱动.
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+mysqlconnector://root:password@127.0.0.1/books_demo"
# 跟踪数据库的修改 --> 不建议开启 , 一是消耗性能 , 二是未来的版本中会移除.
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.secret_key = "hwhefsewljfejrlesjfl" # 没设置secret_key会有报错提醒
# 将app作为参数传入这个关联工具 , 创建一个两者相关联对象db
db = SQLAlchemy(app) # 注意: web框架里面的模型类基本都是要继承自导入的模块中的某个父类 , 这样才会起到关联的作用.
class Author(db.Model):
"""创建作者子类"""
__tablename__ = "authors" # 定义表名
# 定义字段
# db.Column表示是一个字段 , db.Integer就代表id这个字段的数据类型是整数 , primary_key代表主键(主关键字) , 是作为表的行的唯一标识.
# db.String代表是字符串类型 , 字符串长度定义个n个字节 , unique(唯一的) , unique=True代表这列不允许出现重复的值.
id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(64),unique=True) # string的长度随便写个2的倍数就行了
# 在"一对多"的一中定义author_book属性 , 该属性不会出现在字段中 , 后面的backref="author"是给Book反向引用的
# 由于是"一对多" , 所以"多"的地方用Book参数 , "一"的地方用不加s的实例对象参数author.
author_book = db.relationship("Book",backref="author")
def __repr__(self):
"""返回定制消息, 与__str__作用类似"""
return "Author: %d %s"%(self.id,self.name) class Book(db.Model):
"""创建书籍子类"""
__tablename__ = "books"
id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(64),unique=True)
author_id = db.Column(db.Integer,db.ForeignKey("authors.id")) # 表名.id 来建立外键关联
def __repr__(self):
return "Book: %d %s"%(self.id,self.name) class TrueForm(FlaskForm):
"""表单扩展常用的模型(类)有三种: StringField, PasswordField, SubmitField , 这里只用到两种
然后传入参数并创建出各自的实例对象 , 以供其它地方使用.
"""
author = StringField("作者",validators=[DataRequired()])
book = StringField("书籍",validators=[DataRequired()])
submit = SubmitField("添加") def make_author_book():
author1 = Author(name="金庸")
author2 = Author(name="古龙")
author3 = Author(name="鲁迅")
author4 = Author(name="巴金")
db.session.add_all([author1,author2,author3,author4])
db.session.commit()
book1 = Book(name="<<射雕英雄传>>", author_id=author1.id)
book2 = Book(name="<<天龙八部>>", author_id=author1.id)
book3 = Book(name="<<鹿鼎记>>", author_id=author1.id)
book4 = Book(name="<<笑傲江湖>>", author_id=author1.id)
book5 = Book(name="<<武林外史>>", author_id=author2.id)
book6 = Book(name="<<萧十一郎>>", author_id=author2.id)
book7 = Book(name="<<小李飞刀>>", author_id=author2.id)
book8 = Book(name="<<狂人日记>>", author_id=author3.id)
book9 = Book(name="<<阿Q正传>>", author_id=author3.id)
book10 = Book(name="<<家>>", author_id=author4.id)
book11 = Book(name="<<春>>", author_id=author4.id)
book12 = Book(name="<<秋>>", author_id=author4.id)
db.session.add_all([book1,book2,book3,book4,book5,book6,
book7,book8,book9,book10,book11,book12])
db.session.commit() @app.route("/",methods=["GET","POST"])
def add_author_book():
true_form = TrueForm()
"""
1.调用WTF的函数实现验证
2.验证通过则获取数据
3.判断作者是否存在
4.如果作者存在, 则判断书籍是否存在, 没有重复的书籍就添加数据, 如果重复就提示错误.
5.如果作者不存在, 就添加作者和书籍
6.验证不通过就提示错误.
"""
# 调用WTF的函数实现验证
if true_form.validate_on_submit():
# 2.验证通过则获取此时填入的数据
author_name = true_form.author.data
book_name = true_form.book.data
# 3.判断作者是否存在, Author.query.filter_by(name=author_name)是查询, .first()才是拿到数据.
author_query = Author.query.filter_by(name=author_name).first()
# 4.如果作者存在
if author_query:
book_query = Book.query.filter_by(name=book_name).first() # 查询并拿数据
if book_query:
flash("您要添加的书籍已存在!")
else:
try:
new_book = Book(name="<<%s>>"%book_name,author_id=author_query.id)
db.session.add(new_book)
db.session.commit()
except Exception as e:
flash("添加书籍错误!")
db.session.rollback() # 回滚操作
else:
# 5.如果作者不存在
try:
new_author = Author(name=author_name)
db.session.add(new_author)
db.session.commit()
new_book = Book(name="<<%s>>"%book_name, author_id=new_author.id)
db.session.add(new_book)
db.session.commit()
except Exception as e:
flash("添加作者和书籍错误!")
db.session.rollback()
else:
# 验证不通过
if request.method == "POST":
flash("参数错误!")
# 查询所有的作者信息, 让信息传递给模板
all_authors = Author.query.all()
return render_template("book_manage.html",all_authors=all_authors,form=true_form) # 网页中删除书籍-->将book_id参数传到路由, 路由再将book_id传入delete_book函数内部使用.
# < >尖括号代表路由参数, 路由需要接受参数
@app.route("/delete_book/<book_id>",methods=["GET","POST"])
def delete_book(book_id):
# 1.查询书籍并拿数据
book = Book.query.get(book_id)
try:
db.session.delete(book)
db.session.commit()
except Exception as e:
flash("删除错误!")
db.session.rollback()
# redirect重定向回到根路径, redirect接收路由地址参数, 或者直接接收网址参数(http://xxxxx.com)
# url_for("index"): 需要传入视图函数名, 返回该视图函数对应的路由地址(url)
return redirect(url_for("add_author_book")) # 删除作者
@app.route("/delete_author/<author_id>",methods=["GET","POST"])
def delete_author(author_id):
# 1.查询作者并拿数据
author = Author.query.get(author_id)
try:
# 查询书籍并删除, 直接在查询后面跟 .delete()就可以直接将查询到的结果删除掉
Book.query.filter_by(author_id=author.id).delete()
db.session.delete(author)
db.session.commit()
except Exception as e:
flash("删除错误!")
db.session.rollback() # 回滚
return redirect(url_for("add_author_book")) # 重定向回到根路径 if __name__ == '__main__':
# 先删除所有表, 在创建表之前要先删掉表
db.drop_all()
# 再创建所有表
db.create_all()
make_author_book()
app.run(debug=True)
HTML源代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>图书管理系统 | by-陈彬</title>
</head>
<body>
从此处添加书籍: <br>
<br>
<form method="post">
{{ form.csrf_token() }}
{{form.author.label}} {{form.author}}<br>
{{form.book.label}} {{form.book}}<br>
{{form.submit}}<br>
<br>
{# 显示消息闪现的内容 #}
{% for message in get_flashed_messages() %}
{{message}}
{%endfor%}
</form>
<br>
<hr>
<h1> 书籍目录</h1>
<ul>
<ul>
<ul>
{% for author in all_authors %}
<li>{{author.name}} <a href="{{url_for("delete_author",author_id=author.id)}}">删除</a></li>
<ul>
{% for book in author.author_book %}
<li>{{book.name}} <a href="{{ url_for("delete_book",book_id=book.id) }}">删除</a></li>
{% else %}
<li>无</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
</ul>
</ul>
</body>
</html>
Python-Flask框架之——图书管理系统 , 附详解源码和效果图 !的更多相关文章
- Java开源生鲜电商平台-盈利模式详解(源码可下载)
Java开源生鲜电商平台-盈利模式详解(源码可下载) 该平台提供一个联合买家与卖家的一个平台.(类似淘宝购物,这里指的是食材的购买.) 平台有以下的盈利模式:(类似的平台有美菜网,食材网等) 1. 订 ...
- ArrayList详解-源码分析
ArrayList详解-源码分析 1. 概述 在平时的开发中,用到最多的集合应该就是ArrayList了,本篇文章将结合源代码来学习ArrayList. ArrayList是基于数组实现的集合列表 支 ...
- LinkedList详解-源码分析
LinkedList详解-源码分析 LinkedList是List接口的第二个具体的实现类,第一个是ArrayList,前面一篇文章已经总结过了,下面我们来结合源码,学习LinkedList. 基于双 ...
- Flask框架 之 路由和视图详解
路由+视图 我们之前了解了路由系统是由带参数的装饰器完成的. 路由本质:装饰器和闭包实现的. 路由设置的两种方式 来看个例子. @app.route('/index') def index(): re ...
- Shiro的Filter机制详解---源码分析
Shiro的Filter机制详解 首先从spring-shiro.xml的filter配置说起,先回答两个问题: 1, 为什么相同url规则,后面定义的会覆盖前面定义的(执行的时候只执行最后一个). ...
- Shiro的Filter机制详解---源码分析(转)
Shiro的Filter机制详解 首先从spring-shiro.xml的filter配置说起,先回答两个问题: 1, 为什么相同url规则,后面定义的会覆盖前面定义的(执行的时候只执行最后一个). ...
- udhcp详解源码(序)
最近负责接入模块,包括dhcp.ipoe和pppoe等等.所以需要对dhcp和ppp这几个app的源代码进行一些分析.网上有比较好的文章,参考并补充自己的分析. 这篇udhcp详解是基于busybox ...
- RecyclerView实现瀑布流效果(图文详解+源码奉送)
最近有时间研究了一下RecyclerView,果然功能强大啊,能实现的效果还是比较多的,那么今天给大家介绍一个用RecyclerView实现的瀑布流效果. 先来一张效果图: 看看怎么实现吧: 整体工程 ...
- Android4.3 屏蔽HOME按键返回桌面详解(源码环境下)
点击打开链接 首先声明我是做系统开发的(高通平台),所以下面介绍的方法并不适合应用开发者. 最经有个需求要屏蔽HOME按键返回桌面并且实现自己的功能,发现以前的方式报错用不了,上网搜索了一下,发现都是 ...
随机推荐
- web中间件切换(was切tomcat)
一.数据源迁移: ①数据源配置在web容器还是在项目本身? 根据开发与生产分离原则选择配置到web容器,以免开发泄露数据库密码. ②数据库密码加密 原先was的数据源直接在console控制,密码是密 ...
- (七十五)CoreLocation(一)在iOS7和iOS8设备上获取授权
苹果在iOS8上更新了CoreLocation的授权获取方式,在原来的基础上,不仅需要调用授权函数,还需要对info.plist进行相应的配置. 在iOS上获取经纬度使用的是CoreLocationM ...
- 海量数据挖掘MMDS week7: 相似项的发现:面向高相似度的方法
http://blog.csdn.net/pipisorry/article/details/49742907 海量数据挖掘Mining Massive Datasets(MMDs) -Jure Le ...
- C++ Primer 有感(重载操作符)
1.用于内置类型的操作符,其含义不能改变.也不能为任何内置类型定义额外的新的操作符.(重载操作符必须具有至少一个类类型或枚举类型的操作数.这条规则强制重载操作符不能重新定义用于内置类型对象的操作符的含 ...
- JSP编译成Servlet(四)JSP与Java行关系映射
我们知道java虚拟机只认识class文件,要在虚拟机上运行就必须要遵守class文件格式,所以JSP编译成servlet后还需要进一步编译成class文件,但从JSP文件到java文件再到class ...
- Dynamics CRM2013 1:N关系 sub-grid中的“添加现有项”和“添加新建项”功能详解
CRM2013中sub-grid的样式和2011中有了较大的变化,2013和2011界面对比如下 在2011的时候按钮是在ribbon区,1:N的父子关系实体直接点击添加新纪录就可以,但2013就不行 ...
- java中,用json格式转换遇到问题
将list转为JSONObject类,报 org/apache/commons/lang/exception/NestableRuntimeException是什么原因? 还需要导入这些包common ...
- Linux Shell 命令--grep
从这篇开始,是文本内容操作,区别于文本操作. shell,perl,python,一直都是文本操作的专家语言,而我们今后学习的的将是shell的噱头--文本操作.下面提到最常见的一个: grep 这算 ...
- 友善之臂tiny4412-1306开发板安卓系统烧写
折腾了很久,终于烧写成功.不废话,咱们说说流程吧. 首先,我们需要有一个基于tiny4412的kernel,从友善之臂官网获取. 然后解压: 1.tar -xvf linux-3.5 .... 然后 ...
- java缓存系统
第一版 package cache; import java.util.HashMap; import java.util.Map; public class Cache1 { private Map ...