Flask框架篇(全)
说明:Flask是一个Python编写的Web 微框架,利用它可以使用Python语言快速实现一个网站或Web服务。因为我是从Django框架入手Python Web的,所以在学习Flask的过程中,总是会一边思考同样的功能,Django该如何实现。本文参考自Flask官方文档,大部分代码引用官方文档和自己编写的一些案例,官方文档:http://flask.pocoo.org/docs/1.0/。
一、区分Flask和Django
1.Flask
①Flask自由、灵活,可扩展性强,第三方库的选择面广,开发时可以结合自己最喜欢用的轮子,也能结合最流行最强大的Python库。
②入门简单,即便没有多少web开发经验,也能很快做出网站。
③非常适用于小型网站。
④非常适用于开发web服务的API。
⑤开发大型网站无压力,但代码架构需要自己设计,开发成本取决于开发者的能力和经验。
⑥各方面性能均等于或优于Django 。
⑦Django自带的或第三方的好评如潮的功能,Flask上总会找到与之类似第三方库。
⑧Flask与关系型数据库的配合使用不弱于Django,而其与NoSQL数据库的配合远远优于Django。
2.Django
①Django太重了,除了web框架,自带ORM和模板引擎,灵活和自由度不够高。
②Django能开发小应用,但总会有“杀鸡焉用牛刀”的感觉。
③Django的自带ORM非常优秀,综合评价略高于SQLAlchemy。
④Django自带的模板引擎简单好用,但其强大程度和综合评价略低于Jinja。
⑤Django自带ORM也使Django与关系型数据库耦合度过高,如果想使用MongoDB等NoSQL数据,需要选取合适的第三方库,且总感觉Django+SQL才是天生一对的搭配。
⑥Django非常适合企业级网站的开发:快速、靠谱、稳定。
⑦Django是Python web框架的先驱,用户多,第三方库最丰富,最好的Python库,如果不能直接用到Django中,也一定能找到与之对应的移植。
⑧Django上手也比较容易,开发文档详细、完善,相关资料丰富。
二、Flask开发环境搭建
1.Windows下开发环境搭建
①安装Python
②安装pip
③使用pip安装Flask
pip install flask
2. Linux下开发环境搭建(Ubuntu 16.04 64)
①系统自带Python
②安装pip:sudo apt-get install python-pip
③使用pip安装Flask :pip install flask
3. 配置基于Vim的Python开发环境(插件)
①Vundle
②YouCompleteMe
③NERDTree
④Vim-Jinja2-Syntax
三、在 Flask 中的 "Hello, World"
1.使用pycharm创建flask项目后会自动生成以下目录
├─app.py ├─static └─templates
2.app.py的代码
from flask import Flask app = Flask(__name__) @app.route(‘/‘) def hello_world(): return ‘Hello World!‘ if __name__ == ‘__main__‘: app.run()# 在浏览器输入127.0.0.1:5000后,即可在网页上出现"Hello World"的字样
四、路由
1.Flask路由
在上面的例子里可以看到路由的使用。如果了解Python另一款Web框架Django的话,我觉得应该对路由很熟悉,在Django里使用的是url来正则匹配当前路径,而Flask中路由是通过使用app.route装饰器来设置。
@app.route(‘/‘) def index(): return ‘Index Page‘ @app.route(‘/hello‘) def hello(): return ‘Hello, World‘
2.路径变量
如果希望获取/blog/1这样的路径参数,就需要使用路径变量。路径变量的语法是/path/<converter:varname>。在路径变量前还可以使用可选的转换器,有以下几种转换器。
转换器 | 作用 |
---|---|
string | 默认选项,接受除了斜杠之外的字符串 |
int | 接受整数 |
float | 接受浮点数 |
path | 和string类似,不过可以接受带斜杠的字符串 |
any | 匹配任何一种转换器 |
uuid | 接受UUID字符串 |
3.构造URL
在Web程序中常常需要获取某个页面的URL,在Flask中需要使用url_for(‘方法名‘)来构造对应方法的URL,下面是Flask官方的例子。
>>> from flask import Flask, url_for >>> app = Flask(__name__) >>> @app.route(‘/‘) ... def index(): pass ... >>> @app.route(‘/login‘) ... def login(): pass ... >>> @app.route(‘/user/<username>‘) ... def profile(username): pass ... >>> with app.test_request_context(): ... print url_for(‘index‘) ... print url_for(‘login‘) ... print url_for(‘login‘, next=‘/‘) ... print url_for(‘profile‘, username=‘John Doe‘) ... / /login /login?next=/ /user/John%20Doe
4.HTTP方法
如果需要处理具体的HTTP方法,在Flask中也很容易,使用route装饰器的methods参数设置即可。
from flask import request @app.route(‘/login‘, methods=[‘GET‘, ‘POST‘]) def login(): if request.method == ‘POST‘: do_the_login() else: show_the_login_form()
5.静态文件
Web程序中常常需要处理静态文件,在Flask中需要使用url_for函数并指定static端点名和文件名。在下面的例子中,实际的文件应放在static/文件夹下。
<img src="{{ url_for(‘static‘, filename=‘add.png‘) }}">
五、Flask的模板功能
1.模板功能
Flask模板功能与Django是一样的,所以不需要很长时间就能弄懂模板之时,默认情况下,文件需要放在templates文件夹下。
<!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8"> <title></title></head><body><h1>hello {{ user.user_name }}</h1></body></html>
2.新的模板功能
①编写一个模拟登录的视图函数。
@app.route(‘/query_user/<user_id>‘)def query_user(user = None): return render_template("user_id.html",user=user)
②为了渲染模板,必须从 Flask 框架中导入一个名为 render_template 的新函数。此函数需要传入模板名以及一些模板变量列表,返回一个所有变量被替换的渲染的模板。
③在内部,render_template 调用了 Jinja2 模板引擎,Jinja2 模板引擎是 Flask 框架的一部分。Jinja2 会把模板参数提供的相应的值替换了 {{…}} 块。
3.模板中控制语句
Jinja2 模板同样支持控制语句,像在 {%…%} 块中。在模板中添加一个 if 声明(文件templates/index.html)。
<!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8"> <title></title></head><body>{% if user %} hello {{ user.user_name }}{% else %} no this user{% endif %}</body></html>
4.模板中的循环语句
①为了表示用户的文章,这里使用了列表,其中每一个元素包含 author 和 body 字段。当使用真正的数据库的时候,会保留这些字段的名称,因此在设计以及测试模板的时候尽管使用的是假冒的对象,但不必担心迁移到数据库上更新模板。
def index(): user = { ‘nickname‘: ‘Miguel‘ } posts = [ { ‘author‘: { ‘nickname‘: ‘John‘ }, ‘body‘: ‘Beautiful day in Portland!‘ }, { ‘author‘: { ‘nickname‘: ‘Susan‘ }, ‘body‘: ‘The Avengers movie was so cool!‘ } ] return render_template("index.html", title = ‘Home‘, user = user, posts = posts)
②在模板这一方面,还有一个新问题。列表中可能有许多元素,多少篇文章被展示将取决于视图函数。模板不会假设有多少文章,因此它必须准备渲染视图传送的文章数量。
③因此可以使用 for 来做到这一点(文件templates/index.html)。
<html> <head> {% if title %} <title>{{title}}</title> {% else %} <title>microblog</title> {% endif %} </head> <body> <h1>Hi, {{user.nickname}}!</h1> {% for post in posts %} <p>{{post.author.nickname}} says: <b>{{post.body}}</b></p> {% endfor %} </body></html>
Flask的模板继承
1.可以利用 Jinja2 的模板继承的特点,这允许我们把所有模板公共的部分移除出页面的布局,接着把它们放在一个基础模板中,所有使用它的模板可以导入该基础模板。
2.所以定义一个基础模板,该模板包含导航栏以及上面谈论的标题(文件templates/base.html),这与Django的模板继承也是一回事。
①在这个模板中,我们使用 block 控制语句来定义派生模板可以插入的地方,块被赋予唯一的名字。
<!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8"> <title></title></head><body><div> <h1>Header 蒋振飞的博客</h1></div>{% block content %}{% endblock %}<div> <h1>Footer JzfBlog</h1></div></body></html>
②接着现在剩下的就是修改我们的 index.html 模板继承自 base.html (文件templates/index.html),注意:继承的时候一定要加上双引号。
{% extends "base.html" %}{% block content %} <h2>这是第一页</h2>{% endblock %}
处理请求
在 Flask 中获取请求参数需要使用request等几个全局对象,但是这几个全局对象比较特殊,它们是 Context Locals ,其实就是 Web 上下文中局部变量的代理。虽然我们在程序中使用的是全局变量,但是对于每个请求作用域,它们都是互不相同的变量。理解了这一点,后面就非常简单了
1.Request 对象
简单做一个登录的样例
<!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8"> <title></title></head><body><h1>Hello Login</h1><form action="/login" method="post"> <input type="text" name="username"> <input type="password" name="password"> <input type="submit" value="Submit"></form> <--!用于获取flashed的内容--> <h2>{{ get_flashed_messages()[0] }}</h2></body></html>
那么传到了login视图中,就需要导入request对象来接收这个表单
@app.route(‘/login‘, methods=[‘POST‘])def login(): form = request.form username = form.get(‘username‘) password = form.get(‘password‘) if not username: # 用户名为空 flash("please input username") return render_template("index.html") if not password: # 密码为空 flash("please input password") return render_template("index.html") if username == ‘flask‘ and password == ‘mysql‘: # 用户名与密码等于设定值 flash("login success") return render_template("index.html") else: # 用户名与密码设定值不符 flash("username or password is wrong") return render_template("index.html")
2.文件上传
利用Flask也可以方便的获取表单中上传的文件,只需要利用 request 的files属性即可,这也是一个字典,包含了被上传的文件。如果想获取上传的文件名,可以使用filename属性,不过需要注意这个属性可以被客户端更改,所以并不可靠。更好的办法是利用werkzeug提供的secure_filename方法来获取安全的文件名
from flask import requestfrom werkzeug.utils import [email protected](‘/upload‘, methods=[‘GET‘, ‘POST‘])def upload_file(): if request.method == ‘POST‘: f = request.files[‘the_file‘] f.save(‘/var/www/uploads/‘ + secure_filename(f.filename))
3.Cookies
Flask也可以方便的处理Cookie。使用方法很简单,直接看官方的例子就行了。下面的例子是如何获取cookie
from flask import [email protected](‘/‘)def index(): username = request.cookies.get(‘username‘) # 使用 cookies.get(key) 代替 cookies[key] 避免 # 得到 KeyError 如果cookie不存在
如果需要发送cookie给客户端,参考下面的例子。
from flask import [email protected](‘/‘)def index(): resp = make_response(render_template(...)) resp.set_cookie(‘username‘, ‘the username‘) return resp
4.重定向和错误
redirect和abort函数用于重定向和返回错误页面
from flask import abort, redirect, [email protected](‘/‘)def index(): return redirect(url_for(‘login‘))@app.route(‘/login‘)def login(): abort(401) this_is_never_executed()
默认的错误页面是一个空页面,如果需要自定义错误页面,可以使用errorhandler装饰器。
from flask import [email protected](404)def page_not_found(error): return render_template(‘page_not_found.html‘), 404
5.响应处理
默认情况下,Flask会根据函数的返回值自动决定如何处理响应:如果返回值是响应对象,则直接传递给客户端;如果返回值是字符串,那么就会将字符串转换为合适的响应对象。我们也可以自己决定如何设置响应对象,方法也很简单,使用make_response函数即可
@app.errorhandler(404)def not_found(error): resp = make_response(render_template(‘error.html‘), 404) resp.headers[‘X-Something‘] = ‘A value‘ return resp
6.Sessions
我们可以使用全局对象session来管理用户会话。Sesison 是建立在 Cookie 技术上的,不过在 Flask 中,还可以为 Session 指定密钥,这样存储在 Cookie 中的信息就会被加密,从而更加安全。直接看 Flask 官方的例子
from flask import Flask, session, redirect, url_for, escape, requestapp = Flask(__name__)@app.route(‘/‘)def index(): if ‘username‘ in session: return ‘Logged in as %s‘ % escape(session[‘username‘]) return ‘You are not logged in‘@app.route(‘/login‘, methods=[‘GET‘, ‘POST‘])def login(): if request.method == ‘POST‘: session[‘username‘] = request.form[‘username‘] return redirect(url_for(‘index‘)) return ‘‘‘ <form method="post"> <p><input type=text name=username> <p><input type=submit value=Login> </form> ‘‘‘@app.route(‘/logout‘)def logout(): # remove the username from the session if it‘s there session.pop(‘username‘, None) return redirect(url_for(‘index‘))# set the secret key. keep this really secret:app.secret_key = ‘A0Zr98j/3yX R~XHH!jmN]LWX/,?RT‘
web 表单
1.配置
①为了能够处理 web 表单,我们将使用 Flask-WTF ,该扩展封装了 WTForms 并且恰当地集成进 Flask 中
②许多 Flask 扩展需要大量的配置,因此我们将要在项目的根目录下创建一个配置文件以至于容易被编辑。这就是我们将要开始的(文件 config.py):
CSRF_ENABLED = TrueSECRET_KEY = ‘you-will-never-guess‘
③Flaks-WTF 扩展只需要两个配置。 CSRF_ENABLED 配置是为了激活 跨站点请求伪造 保护。在大多数情况下,你需要激活该配置使得你的应用程序更安全些
④SECRET_KEY 配置仅仅当 CSRF 激活的时候才需要,它是用来建立一个加密的令牌,用于验证一个表单。当编写自己的应用程序的时候,需要设置很难被猜测到密钥
⑤既然有了配置文件,我们需要告诉 Flask 去读取以及使用它。我们可以在 Flask 应用程序对象被创建后去做,方式如下(文件 app.py)
from flask import Flaskapp = Flask(__name__)app.config.from_object(‘config‘)
2.用户登录表单
①在 Flask-WTF 中,表单是表示成对象,Form 类的子类。一个表单子类简单地把表单的域定义成类的变量
②编写第一个表单(文件forms.py):
from wtforms import Form, TextField, PasswordField, validatorsclass LoginForm(Form): username = TextField("username", [validators.Required()]) password = PasswordField("password", [validators.Required()])
③导入 Form 类,接着导入两个们需要的字段类,TextField 和 PasswordField
④validators.Required()验证器只是简单地检查相应域提交的数据是否是空
3.表单模板
①登录的模板(文件templates/login.html)
{% extends "base.html" %}{% block content %}<h1>User Management</h1>{% if message %} {{message}} {% endif %}<form action="" method="post" name="login"> {{form.hidden_tag()}} Username :{{form.username}} <br/> Password :{{form.password}} <br/> <input type="submit" value="Submit" /> <input type="reset" value="reset" /> </form>{% endblock %}
②重用了 base.html 模板通过 extends 模板继承声明语句,以确保所有网页的布局一致性
③form.hidden_tag() 模板参数将被替换为一个隐藏字段,用来是实现在配置中激活的 CSRF 保护。如果已经激活了 CSRF,这个字段需要出现在你所有的表单中
④表单中实际的字段也将会被表单对象渲染,必须在字段应该被插入的地方指明一个 {{form.field_name}} 模板参数。某些字段是可以带参数的
⑤没有在表单中定义提交按钮,因为提交字段实际并不携带数据因此没有必要在表单类中定义
4.表单视图
①新的视图函数(文件views.py):
from flask import render_template, flash, redirectfrom .forms import LoginForm# index view function suppressed for [email protected](‘/login‘, methods = [‘GET‘, ‘POST‘])def login(): form = LoginForm() return render_template(‘index.html‘, form=form)
②已经导入 LoginForm 类,从这个类实例化一个对象,接着把它传入到模板中,这就是渲染表单所有要做的
③这里唯一的新的知识点就是路由装饰器的 methods 参数。参数告诉 Flask 这个视图函数接受 GET 和 POST 请求,如果不带参数的话,视图只接受 GET 请求。
④这个时候可以尝试运行应用程序,在浏览器上看看表单。在运行应用程序后,需要在浏览器上打开 http://localhost:5000/login
5.接收表单数据
@app.route("/user", methods=[‘GET‘, ‘POST‘])def login(): myForm = LoginForm(request.form) if request.method == ‘POST‘: # if form.validate_on_submit(): if myForm.username.data== "flask" and myForm.password.data == "mysql" and myForm.validate(): return redirect("https://www.jzfblog.com") else: message="Login Failed" return render_template(‘index.html‘, message=message, form=myForm) return render_template(‘index.html‘, form=myForm)
②validate_on_submit 方法做了所有表单处理工作。当表单正在展示给用户的时候调用它,它会返回 False.
③如果 validate_on_submit 在表单提交请求中被调用,它将会收集所有的数据,对字段进行验证,如果所有的事情都通过的话,它将会返回 True,表示数据都是合法的。这就是说明数据是安全的,并且被应用程序给接受了
④如果至少一个字段验证失败的话,它将会返回 False,接着表单会重新呈现给用户,这也将给用户一次机会去修改错误。我们将会看到当验证失败后如何显示错误信息
⑤当 validate_on_submit 返回 True,我们的登录视图函数调用了两个新的函数,导入自 Flask。flash 函数是一种快速的方式下呈现给用户的页面上显示一个消息。在我们的例子中,我将会使用它来调试,因为我们目前还不具备用户登录的必备的基础设施,相反我们将会用它来显示提交的数据。flash 函数在生产服务器上也是十分有作用的,用来提供反馈给用户有关的行动
使用Mysql数据库
1.连接Mysql数据库
在项目中创建一个db.py文件,这是通过对sql语句直接针对数据库的操作
# 注:py3使用的是pymysql,py2使用mysqldbimport pymysqlconn = pymysql.connect(host=‘localhost‘, user=‘root‘, password="5201314mysql", database=‘test‘)cur = conn.cursor()def addUser(username, password): """ 添加用户 :param username: 用户名 :param password: 密码 """ insert_sql = "insert into user(username, password) values(‘%s‘, ‘%s‘)" % (username, password) # 提交sql语句 cur.execute(insert_sql) # 保存 conn.commit()def isExisted(username, password): """ 判断用户是否已经存在 :param username: 用户名 :param password: 密码 :return: True or False """ sql = "select * from user where username=‘%s‘ and password=‘%s‘" % (username, password) # 提交sql语句 cur.execute(sql) # 获取查询结果 result = cur.fetchall() if len(result): return True else: return False
2.在项目中创建forms.py,用于登录与注册的表单
from wtforms import Form, TextField, PasswordField, validatorsclass LoginForm(Form): """ 登录表单 """ username = TextField(‘username‘, [validators.Required()]) password = PasswordField(‘password‘, [validators.Required()])class RegisterForm(Form): """注册表单""" username = TextField(‘username‘, [validators.Required()]) password = PasswordField(‘password‘, [validators.Required()]) password_again = PasswordField(‘password‘, [validators.Required()])
3.新建一个views.py文件,用户对视图进行处理
from flask import Flask, render_template, request, redirect, flashfrom forms import *from db import *app = Flask(__name__)app.config.from_object(‘config‘)@app.route(‘/login‘, methods=[‘GET‘, ‘POST‘])def login(): """ 登录 :return: 登录模板视图 """ # 先创建一个登录的表单 login_form = LoginForm(request.form) if request.method == ‘POST‘: # if login_form.username.data == ‘flask‘ and login_form.password.data == ‘mysql‘ and login_form.validate(): # 如果数据库存在这样的用户名与密码 if isExisted(login_form.username.data, login_form.password.data) and login_form.validate(): # 返回到我的主页 return redirect(‘https://www.jzfblog.com‘) else: message = ‘Login False‘ # 不存在或密码不正确就返回Login False的错误信息 return render_template(‘login.html‘, form=login_form, message=message) return render_template(‘login.html‘, form=login_form)@app.route(‘/register‘, methods=[‘GET‘, ‘POST‘])def register(): """ 注册 :return: 注册模板视图 """ # 创建注册表单 register_form = RegisterForm(request.form) if request.method == ‘POST‘: # 判断第一次输入密码与第二次输入密码是否一致 if register_form.password.data != register_form.password_again.data: message = "两次密码输入不一致" # 返回错误信息 return render_template(‘register.html‘, form=register_form, message=message) else: if not isExisted(register_form.username.data, register_form.password.data): # 如果数据库没有这样的用户,就在数据库新增一个 # addUser是db.py的操作数据库添加用户的函数 addUser(register_form.username.data, register_form.password.data) return redirect(‘https://jzfblog.com‘) message = "账号已存在,请重新注册" # 如果数据库已经存在这个用户,就返回已存在的信息 return render_template(‘register.html‘, form=register_form, message=message) return render_template(‘register.html‘, form=register_form)if __name__ == "__main__": app.run()
4.在templates模板文件夹下创建login.html
<div style="width:100%;height:20px;text-align: center;"> <h1>欢迎登录</h1> {{ message }} <form action="/login" method="post"> {# {{form.hidden_tag()}}#} <label for="username">用户名:</label> {{ form.username }} <label for="password">密码:</label> {{ form.password }} <input type="submit" value="submit"/> </form></div>
5.在templates模板文件夹下创建register.html
<div> <h1>欢迎注册</h1> {{ message }} <form action="/register" method="POST"> {# {{form.hidden_tag()}}#} <label for="username">用户名:</label> {{ form.username }} <label for="password">密码:</label> {{ form.password }} <label for="password_again">再输一次密码:</label> {{ form.password_again }} <input type="submit" value="register"/> </form></div>
数据库映射
1.何谓对象关系映射
对象关系映射,即Object Relational Mapping,简称ORM,是一种程序设计技术,用于实现面向对象程序设计里不同类型之间系统的数据之间的转换
2. 为什么要使用ORM
①避免和复杂的sql语句打交道
②提高开发效率,也更容易理解
③方便形成统一风格的代码
④带来一些安全性上的提升
3.数据库注入,通过数据库映射就能有效地避免这一现象
百度百科对数据库映射的解释是:用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入
根据理解,简单的用一个例子解释一下。比如之前的用户注册和登录,我在models.py中写了一个判断用户是否存在数据库的方法,也就是isExisted方法,里面使用了
sql = "select * from user where username=‘%s‘ and password=‘%s‘" % (username, password)
其实上面这段查询语句还可以按下面这样写,不过这就会出现问题了,很容易发生sql注入的现象
sql = ‘select * from user where username=‘ + username + ‘ and password = ‘ + password
假设我在登录的时候输入username为1,让password 输入 1 or 1 = 1 ,sql查询语句就会变成这样
select * from user where username = 1 and password =1 or 1 = 1
不知道发现没有,后面有一个or 1 =1,那这就是 一定成立的条件了!也就是数据库一定可以select到,即使没有1这个用户名,也能被登录了(亲测),所以不能使用拼接字符串的方法来实现查询功能
4.使用对象关系映射的优点与缺点
①系统消耗较大
②处理较复杂的查找条件时,使用ORM不够灵活
③占用内存比较大
5.Flask中的ORM
Flask-SQLAlchemy:基于SQLAlychemy的Flask扩展
安装配置Flask-SQLAlchemy
pip install flask-sqlalchemy
在shell终端的python环境测试一下导入这个包
from flask.ext.sqlalchemy import SQLAlchemy
我的这里仍然会出现一个No module named ‘flask.ext‘的错误,查了一下,解决需要改变一种导入方式
from flask_sqlalchemy import SQLAlchemy
6.创建models.py文件,用来定义数据库模型
# model.pyfrom flask import Flask# from flask.ext.sqlalchemy import SQLAlchemyfrom flask_sqlalchemy import SQLAlchemyapp = Flask(__name__)# 如果数据库没有密码,直接mysql://@localhost/test就可以了app.config[‘SQLALCHEMY_DATABASE_URI‘] = ‘mysql://root:/test‘db = SQLAlchemy(app)class User(db.Model): id = db.Column(db.Integer,primary_key=True) username = db.Column(db.String(32),unique=True) password = db.Column(db.String(32)) def __init__(self,username,password): self.username = username self.password = password def add(self): """ 增加用户 :return: 成功返回0,失败返回错误信息 """ try: db.session.add(self) db.session.commit() return self.id except Exception,e: db.session.rollback() return e finally: return 0 def isExisted(self): """ 判断用户是否存在 :return: 存在返回1,不存在返回0 """ # object.query.filter_by来筛选条件 temUser=User.query.filter_by(username=self.username,password=self.password).first() if temUser is None: return 0 else: return 1
所以现在,如果满足创建用户的条件就改为
u = User(register_form.username.data,register_form.password.data)u.add()
满足登录条件
u = User(login_form.username.data,login_form.password.data)if (u.isExisted()): return redirect("https://jzfblog.com")