-
是干嘛的?
一个web框架,基于Python,可以快速搭建web后端
-
优点
- 基于Python,开发效率很高
- 默认提供Jinija2模板引擎,如果仅仅是开发小型web,那么flask可以把前端也一起包了
- 可以很方便地 *** 作数据库
-
本文要讲什么
- 简单介绍一下flask框架,并且通过三个小示例说明如何使用flask开发简单的前后端不分离web应用。小示例如下:
- 开发用户注册和登录模块,不同的用户对数据库有不同的 *** 作权限(浏览数据库和编辑数据库)。(提示:在注册时将有关信息保存到数据库中;当再次登录时,根据用户输入的用户名和密码在数据库中查找,不同的用户给出不同的页面进行 *** 作。)
- 开发一个简单的在线考试程序,可以包括若干道单选题、多选题,单击交卷 按钮后就可以根据标准答案在线评分。
- 开发一个页面,当客户第一次访问时,需在线注册姓名、性别等信息,然后将信息保存到 Cookies 中,当下次如该用户再访问,则显示“某某,您好,您是第几次光临本站”的欢迎信息。
- 简单介绍一下flask框架,并且通过三个小示例说明如何使用flask开发简单的前后端不分离web应用。小示例如下:
- 由于我安装了一个丰富的anaconda,故许多包都不需要再手动安装。如果有需要的话,pycharm中安装软件包的方法如下:
-
flask的开发推荐使用pycharm进行。如果没有的话,vscode也可以。本文使用pycharm进行开发。
-
新建项目,选择flask,选择环境(注:后续所有pip install全部是装在此环境中。如果是练习,建议共用一个环境;如果要开发一个项目,建议选择一个新环境),点击create。这样就创建了一个新项目。
-
创建成功后可以看到目录结构如下图所示。简单介绍一下,static用于存放静态资源(如css文件、js文件等等),templates用于存放Jinijia模板(html文件),现在都是空的
.py为结尾的全部是后端的文件。
点击运行,浏览器打开http://127.0.0.1:5000/(5000是默认端口,可以修改的),可以看到Hello World。好耶!-
解释一下这里的逻辑:
app.route是一个映射器,将url和函数进行一一映射
代码中,app.route将/映射为hello_world函数,故访问http://127.0.0.1:5000/,实际上相当于执行了hello_world函数
hello_world返回了一个字符串,Jinija将这个字符串渲染为一个网页,于是在页面上呈现了字符串的内容
-
-
debug选项的配置
默认情况下pycharm不打开debug配置,这种情况下改动代码必须重新运行项目才能将改动反映到localhost上
开发过程中可以在下面打开debug选项,flask会在每次保存代码的时候自动的重新载入代码,并且如果代码有错误,会在终端进行提示,便于随时调试代码
-
上面的例子中,我们可以在页面上看到Hello World。为什么呢?要回答这个问题首先来看两个东西:
-
@app.route
@app.route是装饰器,用于将url映射到一个函数(也就是紧随其后的那个函数)。当浏览器访问这个url时,flask后端就调用这个函数
-
视图函数
这种被@app.route进行映射的函数就是视图函数。之所以叫这个名字,因为浏览器端的页面展示完全取决于这个函数的返回值
-
开发小型web页面显然不能仅仅返回字符串,而要返回一个像样一点的html页面。那么我们就不能只靠视图函数的return来实现了
我们可以在template文件夹中写一些html代码,并且在static目录下写一些配套的css、js代码,最后在视图函数中返回这个页面即可
-
下面简单的写了一个html文件,放在template中,随便配了点样式:
<form action="/login-check" method="post" class="container"> <div class="form"> <span>用户名:span> <input type="text" name="username" placeholder="请输入用户名"> div> <div class="form"> <span>密 码:span> <input type="password" name="password" placeholder="请输入密码"> div> <button type="submit">登录button> form>
那么,要如何让用户访问http://127.0.0.1:5000/login/时看到这个html文件呢?可以在视图函数的return返回render_template渲染的Jinija模板,如下:
然后访问login,发现展示了这个页面
- 用户输入表单数据后,后端如何拿到呢?
-
首先,前端form表格的action位置填入一个url参数。本示例中填入action=“/login-check”,表示表单提交到这个url
-
后端使用@app.route装饰器配置一个对应的url以及对应的函数。从flask中import request,即可在函数中使用request获取前端发过来的请求,包括表单信息。如下:
注意:
- request必须从flask中import才能使用
- @app.route装饰器需要配置post方法,才能响应post请求。否则,默认只能响应get请求
- request中可以拿到所有请求信息,request.form可以拿到字典格式的表单数据,字典中的key为input框的name属性(因此,前端form中的输入框必须配置name属性)
-
拿到表单数据以后,我们就需要进行一些数据库 *** 作:登录时,需要在数据库查找用户是否存在、密码是否正确;注册时,需要将用户信息插入数据库中
-
那么,flask如何 *** 作数据库呢?
flask中可以使用
SQLAlchemy
库 *** 作sql。SQLAlchemy
是一个数据库的ORM
框架,让我们 *** 作数据库的时候不要再用SQL
语句了,跟直接 *** 作模型一样本文中不使用
SQLAlchemy
库,而使用Flask-SQLAlchemy
库进行 *** 作。Flask-SQLAlchemy
是对SQLAlchemy
进行了一个简单的封装,使得我们在flask
中使用sqlalchemy
更加的简单。可以通过pip install flask-sqlalchemy
本文使用本地的mysql数据库
-
安装包
数据库 *** 作需要用到一些包,需要手动进行安装。主要有flask_sqlalchemy和pymysql包
安装完成后,在app.py中引入from flask_sqlalchemy import SQLAlchemy
-
连接数据库
使用数据库的第一步是需要连接到数据库。如下:
首先配置用户名、密码、数据库名,然后把这些全部塞到mysql的url中。最后,设置SQLALCHEMY_DATABASE_URI配置项的值为url,即可进行连接。db = SQLAlchemy(app)后,db就可以拿到这个数据库
user = '用户名' password = '密码' database = '数据库名称' DB_URI = 'mysql+pymysql://%s:%[email protected]:3306/%s' % (user, password, database) app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI db = SQLAlchemy(app)
-
创建表
flask中,需要通过类来 *** 作数据表的,一个类代表着一张表。类的定义可以参考下面的格式:
class User(db.Model): id = db.Column(db.Integer, primary_key=True,autoincrement=True) username = db.Column(db.String(50), nullable=False) password = db.Column(db.String(50), nullable=False) root = db.Column(db.Integer, default=0) db.create_all() # 创建所有表
首先,类要继承db.Model(最关键!)。然后,即可开始定义每个列的内容。db.Column中可以传入的各种约束还有很多,可以上网搜索一下
定义完所有表以后,可以使用db.create_all()创建所有表(也就是所有继承db.Model)。当然了,也可以在数据库中建好表。但是不管建不建表,都要写类,否则无法进行插入 *** 作
-
插入新数据
数据库 *** 作增删改都是通过db.session.xxx完成的。做完 *** 作以后,使用db.session.commit提交 *** 作(有点类似于git)
例如,随意在User表中插入一条数据,如下:
# db.create_all() admin = User(username="root", password="123456", root=1) db.session.add(admin) db.session.commit()
-
查询数据
查询 *** 作是使用Model.query,如下:
-
其余数据库 *** 作不再演示,查查资料吧 ~
-
前端、后端、数据库都提到了,下面开始写第一个任务。由于仅仅是演示一下flask的使用,故旨在做通,至于前端美不美观、表单的各项验证什么的都不考虑啦
-
任务1:开发用户注册和登录模块,不同的用户对数据库有不同的 *** 作权限(浏览数据库和编辑数据库)。(提示:在注册时将有关信息保存到数据库中;当再次登录时,根据用户输入的用户名和密码在数据库中查找,不同的用户给出不同的页面进行 *** 作。)
-
思路:
- 编写一个登录页面,可以输入用户名、密码进行登录,并且配一个“去注册”按钮
- 编写一个注册页面,可以输入用户名、密码、是否为管理员进行注册,注册完成后自动跳转登录页面
- 登录后,进入首页,如果是用户,则显示欢迎信息;如果是管理员,则展示所有用户的列表
开始吧!
-
登录逻辑处理
简单来说就是用用户名去查数据库,如果查不到则登录失败,如果查到了再检查密码
登录失败后,在页面上报错;登录成功后,转到index页面,并且根据用户权限不同而显示不一样的内容
后端代码编写如下:
- 注意,代码中的render_template、redirect、url_for在使用前都需要从Flask包中引入
userinfo = {} # userinfo记录在全局变量中 @app.route('/login/', methods=['GET', 'POST']) def login(): if request.method == 'GET': return render_template('login.html', **{'msg': '您好,请登录'}) elif request.method == 'POST': user = User.query.filter_by( username=request.form['username'], password=request.form['password'] ).first() if user == None: return render_template('login.html', **{'msg': '登录失败,请检查用户名和密码'}) userinfo['username'] = user.username userinfo['root'] = user.root userinfo['user_list'] = [] return redirect(url_for('index')) @app.route('/index/') def index(): if userinfo['root'] == 1: user_list = User.query.all() for i in user_list: userinfo['user_list'].append(i.username) return render_template('index.html', **userinfo)
简单讲一下编写逻辑:
首先,设置login可以接受get和post请求。函数中,使用request.method获取请求类型(注意必须大写),然后分别进行处理
- get请求用于处理浏览器访问,直接返回页面即可
- post请求用于处理表单提交,需要对表单进行验证,获取数据,返回错误提示/转到首页
在render_template函数中可以看到我们传递了第二个参数:**字典。这表示向html页面传递一些参数,**表示将字典展开。具体含义见下一小点:登录前端页面编写
在post处理逻辑中,首先使用输入的用户密码去查找数据库。这里选择query.filter_by方法
如果查不到,说明登录时输入的用户/密码错误,则返回msg提示登录失败
如果查到了,修改userinfo的值,路由至index页面。进入index后视是否为管理员决定是否从数据库中取出所有用户数据
-
注册逻辑编写
@app.route('/register/', methods=['GET','POST']) def register(): if request.method == 'GET': return render_template('register.html', **{'msg': '您好,请注册'}) elif request.method == 'POST': print(request.form) if len(request.form['username']) == 0 or len(request.form['password']) == 0: return render_template('login.html', **{'msg': '请完整填写信息!'}) user = User.query.filter_by( username=request.form['username'] ).first() if user != None: return render_template('login.html', **{'msg': '注册失败,用户名已存在'}) # 插入数据库 user = User(username=request.form['username'], password=request.form['password'], root=1 if 'root' in request.form else 0) db.session.add(user) db.session.commit() return redirect(url_for('login'))
注册逻辑和登录逻辑差不多。简单讲一下:
- 如果是get请求,返回注册页面
- 如果是post请求,表示用户提交表单进行注册。首先检验表单是否为空,然后查询数据库看看用户名是否重复,最后再检查无误后插入数据库,并且重定向至,login界面
-
登录界面编写
前端界面的编写最需要讲一讲的是如何拿到后端传过来的数据。login.html编写如下:
<form action="{{ url_for('login') }}" method="post" class="container"> <div class="form"> <span>用户名:span> <input type="text" name="username" placeholder="请输入用户名"> div> <div class="form"> <span>密 码:span> <input type="password" name="password" placeholder="请输入密码"> div> <button type="submit">登录button> form> <a href="{{ url_for('register') }}"><button>去注册button>a> <span style="color: red;">{{ msg }}span>
Jinija语法中,可以使用{{Python函数或者变量}}来获取后端传来的东西
form action="{{ url_for(‘login’) }}"表示表单提交到login函数对应的那个url上。在flask中,这是一种比较好的习惯,因为url可能会在以后被更改,但是视图函数的名称一般不会轻易更改。因此,需要用url的地方可以全部使用url_for(视图函数名)进行代替
{{ msg }}表示取后台传回的信息并且放到span标签中
效果如下。进入页面后,提示请登录。如果登录信息错误,会进行提示;如果登录正确,则直接进入index页面
-
index页面编写
这个页面故意用了很多Jinija模板语法
- {% if xxx %}、{% for xx in xx %}都是控制语句的写法(实际上相当于插入了一些py代码)。更详细的用法可以去官网搜索
- user_list是login函数返回的render_template中携带的数据
- {{ user_list | length }}是过滤器语法,表示获取user_list (这是一个数组)的长度。{{}}中不能直接调用py中的len函数
最终效果见上一条
<body> {% if root == 1 %} <div> <h1>欢迎系统管理员h1> <span>用户列表如下:span> <ul> {% for name in user_list %} <li>{{ name }}li> {% endfor %} ul> <span>总用户:{{ user_list | length }}人span> div> {% else %} <h1>欢迎用户 {{ username }}h1> {% endif %} body>
-
随便写点啦。直接上代码:
-
数据库表类:
class Exam(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) title = db.Column(db.String(200), nullable=False) a = db.Column(db.String(100), nullable=False) b = db.Column(db.String(100), nullable=False) c = db.Column(db.String(100), nullable=False) ans = db.Column(db.String(2), nullable=False)
很简单的写了点东西,title是题目内容,a、b、c分别是三个选项,ans是答案
当然了,真正的考试系统肯定不能这么存东西。这里能用就行啦
-
后端:
@app.route('/exam/', methods=['GET', 'POST']) def exam(): data = Exam.query.all() exam_list = [] for i in data: exam_list.append({ 'id': i.id, 'title': i.title, 'a': i.a, 'b': i.b, 'c': i.c, }) if request.method == 'GET': return render_template('exam.html', **{'exam_list': exam_list}) elif request.method == 'POST': score = 0 print(request.form.to_dict()) for key, value in request.form.to_dict().items(): item = data[int(key) - 1] if int(key) == item.id and value == item.ans: score += 1 return render_template('exam.html', **{ 'exam_list': exam_list, 'msg': '提交成功', 'score': score, })
主要思路还是分get和post请求进行处理
首先,去数据库取出所有题目
如果是get请求,直接把数据和页面返回
如果是post请求,说明用户提交了题目答案。那么,对答案进行解析,看看有多少题正确,并且返回相应显示内容
-
前端:
主要是拿到后端传来的数据并且进行展示
<div> <h1>欢迎考试h1> <form action="{{ url_for('exam') }}" method="post" class="container"> {% for exam in exam_list %} <div>{{ loop.index }}. {{ exam["title"] }}<span style="color: red;">{{ right }}span>div> <label style="display: block;"><input type="radio" value="a" name="{{ exam['id'] }}">a. {{ exam["a"] }} label> <label style="display: block;"><input type="radio" value="b" name="{{ exam['id'] }}">b. {{ exam["b"] }} label> <label style="display: block;"><input type="radio" value="c" name="{{ exam['id'] }}">c. {{ exam["c"] }} label> {% endfor %} <button type="submit" style="display: block;">提交button> form> <span>提示:<span style="color: red;">{{ msg }}span>span> <div>总分数:<span style="color: red;">{{ score }}span>div> div>
-
最终效果:
写完答案再提交后,可以看到提示和总分数(提交后页面会刷新)
-
注:flask向前端发送信息,可以使用消息闪现机制。这里不再展开
-
这里偷个小懒,做成这样:用户第一次访问,提示请输入姓名;此后,每次访问都d出“某某,您好,您是第几次光临本站”
-
读取cookie:浏览器在每次访问url时,请求中会自动带上cookie。故后端只需要使用request.cookies.get(“cookie项的名字”)即可拿到cookie
-
写cookie:后端可以构造一个响应体,在响应体中使用set_cookie函数进行cookie信息设置
-
后端代码:
@app.route('/', methods=['GET', 'POST']) def welcome(): # put application's code here if request.method == 'GET': name = request.cookies.get("name") # 判断cookie是否已经存在 if name == None: return render_template('welcome.html', **{ 'msg': '您好,您是首次光临本站,请填写用户名', 'name': '', }) else: time = request.cookies.get("time") time = str(int(time) + 1) resp = make_response(render_template('welcome.html', **{ 'msg': name + ',您好,您是第' + time + '次光临本站', 'name': name, })) resp.set_cookie("time", time, max_age=3600) return resp elif request.method == 'POST': name = request.form['name'] time = '1' resp = make_response(render_template('welcome.html', **{ 'msg': name + ',您好,您是第' + time + '次光临本站', 'name': name, })) resp.set_cookie("name", name, max_age=3600) resp.set_cookie("time", time, max_age=3600) return resp return '出错啦'
简单进行一下逻辑解释。进来后,如果是get请求,则查看cookie中是否有name信息。如果没有,说明是首次登录,则返回“您好,您是首次光临本站,请填写用户名”的信息。否则,time+1,设置响应体,返回响应体
如果是post请求,说明用户填写了input表格。那么我们把这个name写入cookie中,并且写入time为1表示第一次登录,最后返回响应体
-
前端页面:
<body> <div>{{ msg }}div> <br/> {% if name == '' %} <form action="{{ url_for('welcome') }}" method="post" class="container"> <input type="text" name="name" placeholder="请输入用户名"> <button type="submit">提交button> form> {% endif %} body>
简单写了一下。大概就是,展示msg,并且在name为空时展示input框(用于发送post请求)
-
最终效果:
首次访问:
输入用户名,提交后,跳转至欢迎界面:
进行疯狂刷新:
可以看到cookie信息确实存入了浏览器
-
这一章仅仅简单提一句蓝图,讲的也不是很详细。可以跳过!
-
拿到表单数据后的下一步应该是 *** 作数据库,但是这里简单插入对蓝图的讲解
可以看到,现在的app.py中有三个函数,分别是根路径视图函数、login路径视图函数和登录表单处理函数。现在已经略显凌乱了,可以想象,后续开发了很多页面以后,有各种各样的视图函数和功能函数,全部放在app.py中显然是不合理的,也不方便维护解决方法是:引入蓝图,将不同的函数放入不同的文件,最后再在app.py中注册,实现项目的模块化
-
首先,我们需要在项目的根路径下建立一个py包(名称不限)。建好后,默认出现一个__init__.py文件
-
然后,我们在这个blueprint包中新建一个login.py。后续所有和登录有关的逻辑全部写入这个文件中
- 首先,定义一个bp,表示本蓝图。其中,url_prefix参数指的是该蓝图的url的前缀。下图中,定义url_prefix为’/login’,那么访问http://127.0.0.1:5000/login/时会调用login视图函数(为什么?因为前缀是/login,login函数绑定的url是/)
- 然后,在__init__.py中导入login中的bp,方便在外面访问
-
最后,在app.py中注册该蓝图即可。注册时使用register_bluprint函数,如下图
-
这里就是简单提一句蓝图。实际上官网项目的目录结构不止这些
因为后面还要引入数据库等等,要考虑很多东西,故下面就一股脑全写在app.py中了。大家要知道,这样做小东西可以,绝对不要这样开发大项目。开发大项目还是要注意目录规范喔!^_^
- 本文主要是简单介绍了一下flask的原理和使用。许多案例并不细致,甚至有很多地方并不符合工程开发规范。仅仅当作是一个简单的入门文章啦!如果有想交流的东西欢迎在评论区讨论!感谢感谢!^_^
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)