使用flask框架实现简单的图书管理(python 3.8)

前言

这几天学习了flask的框架,这里总结一些学习的历程

一、flask是什么?

flask是轻量级的web框架。 浏览器作为client发出HTTP请求,而web服务器负责处理逻辑,而flask帮助我们完成了安全性和数据流的控制,让我们只用关注于业务逻辑本身,避免重复造轮子

请求
浏览器/client
web服务器

二、学习步骤

1.我的第一个flask程序

需要注意的点:
(1) 此flask程序运行在flask提供的简易服务器上,flask包括路由模块与模板引擎两个部分。所以需要提供服务器地址,@app.route(’/’)就提供了地址,默认是根目录,且默认是支持GET,再后面的WTF表单使用上我们还能用到POST(其实就是类似于输入账号密码)然后得到反馈。
(2)路由模块之后紧接着就是模板引擎,模板引擎可以返回一个字符串或者是一个html文件,html文件需要render_template模块的支持。而html文件中为了在页面中显示后端的传来的数据,需要使用变量代码块以及控制代码块,通常有两个花括号,参考下方的html代码

代码如下:

#导入flask扩展
from flask import  Flask,render_template
from  flask_sqlalchemy import SQLAlchemy
#创建flask应用程序实例,需要传入__name__,为了确定资源所在路径
app = Flask(__name__)
#定义路由以及视图函数,通过装饰器,根路由
#路由默认只支持GET,如果需要增加,需要自行指定
@app.route('/')
def index():
  # return '<h1>Hello World!</h1>'
  #传入网址,模板引擎的使用
  #变量代码块的使用
    url_str ='www.xidian.com'
    my_list=[1,2,3,4,5]
    my_dict={
        'name':'czz' ,
        'url':'www.xidian.com'
    }
    return  render_template('index.html',
                            url_str=url_str,
                            my_list=my_list,
                            my_dict=my_dict)
# <>定义路由参数,<>内需要起个名字
@app.route('/orders/<int:order_id>')
def get_order_id(order_id):
    #需要在视图函数内填入参数名,后面代码才能使用
    print(type(order_id))
    return 'order_id %s'%order_id

if __name__ == '__main__':
    #将Flask程序运行在一个简易服务器上(flask提供)
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
这是模板<br>
这是首页<br>
<!--下面是一个变量代码块的使用-->
{{my_list}}<br>
{{my_list[2]}}<br>
{{my_dict}}<br>
{{my_dict.name}}<br>
{{url_str}}<br>
<hr>
<!--控制代码块,用花括号,基本语法仍然相同-->
{% for num in my_list %}
    {% if num>3 %}
        {{num}}<br>
    {% endif %}
{% endfor %}
<hr>
<!--过滤器-->
{{ url_str | upper}}<br>
{{ url_str | reverse}}<br>
<!--过滤器链式调用-->
{{ url_str | reverse | upper}}<br>
</body>
</html>

结果显示:
在这里插入图片描述

2.实现简单的登录逻辑

问题分析:
(1). WTF为我们封装了登录的相关的逻辑,我们利用wtf来实现表单类,继承自FlaskForm
(2) .我们需要有登录账号,密码,以及密码确认。因此需要username,以及password1,password2,submit。validators是为了表单的验证,如果有表单数据有误会在页面中显示参数错误。
(3).我们为了需要完成与前端的页面的交互,必须利用模板引擎来定义表单类并获取请求的参数
(4)注意利用wtf实现表单类的时候需要设置secret_key进行加密,并在前端代码中加入{{ form.csrf_token() }}
代码如下:

from  flask import  Flask,render_template,request,flash
from flask_wtf import FlaskForm
from  wtforms import StringField,PasswordField,SubmitField
from wtforms.validators import DataRequired,EqualTo
app=Flask(__name__)
app.secret_key='xdu'
#目的:实现简单的登录逻辑处理
#路由需要有get和post两种请求方式,需要判断请求方式
#获取请求参数
#判断参数是否填写以及密码是否相同
#如果判断没有问题则返回一个success
'''
给模板传递消息
flash 需要对内容加密,需要设置secret_key,做加密消息的混淆
模板中需要遍历消息
'''

'''
使用wtf实现表单类
'''
class LoginForm(FlaskForm):
    username=StringField('用户名',validators=[DataRequired()])
    password=PasswordField('密码',validators=[DataRequired(),EqualTo('password','密码不一致')])
    password2 = PasswordField('确认密码')
    submit=SubmitField('提交')
@app.route('/form',methods=['GET','POST'])
def login():
    login_form=LoginForm()
    # 1.request 是一个请求对象-->获取请求方式,数据.
    if request.method == 'POST':
        # 2.获取请求参数
        username = request.form.get('username')
        password = request.form.get('password')
        password2 = request.form.get('password2')
        # 3.wtf验证逻辑的实现
        if login_form.validate_on_submit():
            print(username , password)
            return 'success'
        else:
            flash('参数有误')

    return render_template('index.html',form=login_form)

@app.route('/',methods=['GET','POST'])
def index():
    #1.request 是一个请求对象-->获取请求方式,数据.
    if request.method =='POST':
        #2.获取请求参数
        username =request.form.get('username')
        password =request.form.get('password')
        password2 = request.form.get('password2')
        # 3.判断参数是否相同 密码是否相同
        if not all([username,password,password2]):
            # print('参数不完整')
            flash('参数不完整')
        elif password!=password2:
            # print('密码不一致')
            flash('密码不一致')
        else: return 'success'

    return render_template('index.html')
if __name__ == '__main__':
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post">
    <label>用户名:</label><input type="text" name="username"><br>
    <label>密码:</label><input type="password" name="password"><br>
    <label>确认密码:</label><input type="password" name="password2"><br>
    <input type="submit" value="提交"><br>

    {% for message in get_flashed_messages() %}
        {{message}}
    {% endfor %}
</form>
<hr>
<form method="post">
    {{ form.csrf_token() }}
    {{form.username.label}}{{form.username}}<br>
    {{form.password.label}}{{form.password}}<br>
    {{form.password2.label}}{{form.password2}}<br>
    {{form.submit}}
</form>
</body>
</html>

3.数据库相关配置

这里进行配置数据库,并建立两张表,一张是角色,一张是用户。
User希望有role属性,这个属性的定义需要在另一个模型中定义 使得user可以直接查到role,这样做的目的是为了方便查找,两个模型之间实现了关联

# coding=<encoding name> : # coding=utf-8
from flask import Flask, render_template, redirect, url_for, flash, request
from flask_sqlalchemy import SQLAlchemy
from flask_wtf.csrf import CSRFProtect
import pymysql
app = Flask(__name__)

#设置数据库配置信息
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:123456@127.0.0.1:3306/flask_use_sql'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False #压制警告信息
db=SQLAlchemy(app)
app.config['SECRET_KEY'] = "jfkdjfkdkjf"
'''
需求:两张表
角色(管理员/普通用户)
用户(角色ID)
'''
#数据库模型,需要继承db.Modelf
class Role(db.Model):
    #定义表名
    __tablename__='roles'
    #定义字段 db.Column表示是一个字段
    id=db.Column(db.Integer,primary_key=True)
    name = db.Column(db.String(64), unique=True)
    #在一的一方,写关联
    #表示和User模型发生关联,增加了一个users属性
    user=db.relationship('User', backref='role')

    # 重写__repr__方法,方便查看对象输出内容
    def __repr__(self):
        return 'Role:%s'% self.name

class User(db.Model):
    __tablename__='users'
    id=db.Column(db.Integer,primary_key=True)
    name = db.Column(db.String(16), unique=True)
    email = db.Column(db.String(64), unique=True)
    password = db.Column(db.String(64))
    # db.ForeignKey('roles.id') 表示是外键,需要用 表名.id形式表示
    role_id=db.Column(db.Integer,db.ForeignKey('roles.id'))
    #User希望有role属性,这个属性的定义需要在另一个模型中定义 使得user可以直接查到role 反之亦然
    def __repr__(self):
        return '<User:%s %s %s %s >' % self.name,self.id,self.email,self.password

@app.route('/')
def index():
    return  'hello flask'

if __name__ == '__main__':
    # #删除表
    db.drop_all()
    # #创建表
    db.create_all()

    role=Role(name='admin')
    db.session.add(role)
    db.session.commit()
    user1= User(name='czz',role_id=role.id)
    user2 = User(name='lisi', role_id=role.id)
    db.session.add_all([user1,user2])
    db.session.commit()
    # user.name='czz tcl'
    db.session.commit()
    # db.session.delete(user)
    # db.session.commit()
    # print(user2.role)


    app.run(debug=True)

图书管理的实现

# -*- coding:utf-8 -*-
from flask import Flask, render_template, redirect, url_for, flash, request
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms.validators import DataRequired
from wtforms import StringField,SubmitField
from flask_wtf.csrf import CSRFProtect
#import sys
#reload(sys)
#sys.setdefaultencoding("utf-8")

app=Flask(__name__)

#配置数据库 数据库地址 关闭自动跟踪修改
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:123456@127.0.0.1:3306/flask_project_demo'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False #压制警告信息
app.config['SECRET_KEY'] = "123456"
# #开启csrf保护
# CSRFProtect(app)
#创建数据库对象
db=SQLAlchemy(app)
'''
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/编码/csrf_token
6.实现相关的增删逻辑处理
    a.增加数据
    b.删除书籍 -->网页中删除--点击需要发送书籍的ID给删除书籍的路由-->路由需要接收参数
    url_for的使用 /for else 的使用/ redirect的使用
    c.删除
'''

#定义书和作者模型
#作者模型
class Author(db.Model):
    # 表名
    __tablename__ = 'authors'

    # 字段
    id = db.Column(db.Integer,primary_key=True)
    name = db.Column(db.String(64), unique=True)
    # 关系引用
    # books自己使用,author是给book模型使用的
    books = db.relationship('Book',backref='author')
    def __repr__(self):
        return 'Author:%s' % 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'))

    def __repr__(self):
        return 'Book:%s' % (self.name,self.author_id)

#自定义表单类:
class AuthorForm(FlaskForm):
    author =StringField('作者', validators=[DataRequired()])
    book = StringField('书籍', validators=[DataRequired()])
    submit=SubmitField('提交')

#删除作者
@app.route('/delete_author/<author_id>')
def delete_author(author_id):
    # 查询数据库,是否有改ID的作者,如果有就删除(先删除书后删除作者)
    # 1.查询数据库
    author =Author.query.get(author_id)

    # 2.如果有就删除
    if author:
        try:
            # 查询后直接删除
            Book.query.filter_by(author_id=author_id).delete
            # 删除作者
            db.session.delete(author)
            db.session.commit()
        except Exception as  e:
            print(e)
            flash('删除作者错误')
            db.session.rollback()
    else:
        # 没有提示错误
        flash('作者找不到')
    return redirect(url_for('index'))

# 删除书籍 -->网页中删除--> 点击需要发送书籍的ID给删除书籍的路由-->路由需要接收参数
@app.route('/delete_book/<book_id>')
def delete_book(book_id):
    # 1.查询数据库是否有该id的书,如果有就删除,没有就提示错误
    book=Book.query.get(book_id)
    # 2.如果有就删除
    if book:
        try:
            db.session.delete(book)
            db.session.commit()
        except Exception as  e:
            print(e)
            flash('删除书籍错误')
            db.session.rollback()
    else:
        # 3.没有提示错误
        flash('书籍找不到')
    # 如何返回当前网址——>重定向

    # redirect:重定向,需要传入网址/路由地址
    # url_for:需要传入视图函数名,返回该视图函数对应的路由地址
    return redirect(url_for('index'))# 结果和redirect('/')一样

@app.route('/',methods=['GET','POST'])
def index():
    #创建自定义的表单类
    author_form=AuthorForm()
    '''
        验证逻辑:
        1.调用wtf的函数实现验证
        2.验证通过获取数据
        3.判断作者是否存在
        4.如果作者存在,判断书籍是否存在,如果没重复就添加数据,反之提示错误
        5。作者不存在,添加作者和书籍
        6.验证不通过就提示错误
    
    '''

    #1.调用WTF的函数实现验证
    if author_form.validate_on_submit():
        #2.通过验证获取数据
        author_name=author_form.author.data
        book_name = author_form.book.data
        #3.判断作者是否存在
        author=Author.query.filter_by(name=author_name).first()
        #4.如果作者存在
        if (author) :
            # 判断书籍是否存在
            book=Book.query.filter_by(name=book_name).first()
            if (book):
                flash('存在同名书籍')
            #没有重复的书籍就添加数据
            else:
                try:
                    new_book=Book(name=book_name,author_id=author.id)
                    db.session.add(new_book)
                    db.session.commit()
                except Exception as e:
                    print(e)
                    flash('添加书籍失败')
                    db.session.rollback()
            pass
        else:
            #5.作者不存在,添加作者和书籍
            try:
                new_author = Author(name=author_name)
                db.session.add(new_author)
                db.session.commit()

                new_book = Book(name=book_name, author_id=new_author.id)
                db.session.add(new_book)
                db.session.commit()
            except Exception as e:
                print(e)
                flash('添加作者和书籍失败')
                db.session.rollback()
            pass

        pass
    else:
        if request.method == 'POST':
            flash('参数不全')
    # 查询所有的作者信息,让信息查给模板
    authors = Author.query.all()
    return render_template('library.html', authors=authors, form=author_form)

if __name__ =='__main__':
    # 为了演示方便,先删除所有表,再创建
    db.drop_all()
    db.create_all()

    # 添加测试数据库
    # 生成数据
    au1 = Author(name='隔壁老王')
    au2 = Author(name='老李')
    au3 = Author(name='二营长')
    # 把数据提交给用户会话
    db.session.add_all([au1, au2, au3])
    # 提交会话
    db.session.commit()

    bk1 = Book(name='learn', author_id=au1.id)
    bk2 = Book(name='science', author_id=au1.id)
    bk3 = Book(name='art', author_id=au2.id)
    bk4 = Book(name='beautiful', author_id=au3.id)
    bk5 = Book(name='handsome', author_id=au3.id)
    # 把数据提交给用户会话
    db.session.add_all([bk1, bk2, bk3, bk4, bk5])
    # 提交会话
    db.session.commit()
    app.run(debug=True)
    
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post">
    {{ form.csrf_token()}}
    {{ form.author.label()}}{{form.author}}
    {{ form.book.label()}}{{form.book}}
    {{ form.submit }}
<!--    显示消息闪现的内容-->
    {% for message in get_flashed_messages() %}
        {{message}}
    {% endfor %}
</form>
<hr>
<ul>
<!--    #先遍历作者,然后在作者里遍历书籍-->
    {% for author in authors %}
        <li>{{ author.name }}<a href="{{url_for('delete_author',author_id=author.id)}}">删除</a> </li></li>
        <ul>
            {% for book in author.books %}
             <li>{{ book.name }}<a href="{{url_for('delete_book',book_id=book.id)}}">删除</a> </li>
            {% else %}
                <li></li>
            {% endfor %}
        </ul>
    {% endfor %}
</ul>

</body>
</html>

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值