Python Web 开发框架Flask快速入门
WEB数据库系统
Web 2.0专注于让网站上的用户生成内容,自从它开始,网络编程就成为了热门话题。一直以来都可以使用Python创建网站,但这是一个相当无聊的任务。因此,有很多框架和工具被创造出来,帮助开发人员创建更快,更强大的网站。本文描述了一些将Python和Web服务器组合以创建动态内容的方法。这不是一个完整的介绍,因为这个话题过于广泛,不可能在一篇文章中讲清楚。所以本次课程主要讲解PythonWeb框架中常用的Flask框架的使用。
了解框架:
Flask作为Web框架,它的作用主要是为了开发Web应用程序。那么我们首先来了解下Web应用程序。Web应用程序 (World Wide Web)诞生最初的目的,是为了利用互联网交流工作文档。
一切从客户端发起请求开始。所有Flask程序都必须创建一个程序实例。
当客户端想要获取资源时,一般会通过浏览器发起HTTP请求。
此时,Web服务器使用一种名为WEB服务器网关接口的WSGI(Web Server Gateway Interface)协议,把来自客户端的请求都交给Flask程序实例。
Flask使用Werkzeug来做路由分发(URL请求和视图函数之间的对应关系)。根据每个URL请求,找到具体的视图函数。
在Flask程序中,路由一般是通过程序实例的装饰器实现。通过调用视图函数,获取到数据后,把数据传入HTML模板文件中,模板引擎负责渲染HTTP响应数据,然后由Flask返回响应数据给浏览器,最后浏览器显示返回的结果。
安装框架
pip install flask
最小的Flask应用程序
编写代码
一个最小的 Flask 应用看起来会是这样,找到一个目录新建一个Python文件,比如叫做 helloFlask.py,编写代码如下:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
运行
运行Flask项目只需要执行编写的Python文件即可:
python helloFlask.py
运行成功会看到如下页面:
在浏览器访问:
外部可访问的服务器
如果你运行了这个服务器,你会发现它只能从你自己的计算机上访问,网络中其它任何的地方都不能访问。在调试模式下,用户可以在你的计算机上执行任意 Python 代码。因此,这个行为是默认的。
如果你禁用了 debug 或信任你所在网络的用户,你可以简单修改调用 run() 的方法使你的服务器公开可用,如下:
app.run(host='0.0.0.0')
这会让操作系统监听所有网络。
路由
现代 Web 应用的 URL 十分优雅,易于人们辨识记忆,这一点对于那些面向使用低速网络连接移动设备访问的应用特别有用。如果可以不访问索引页,而是直接访问想要的那个页面,他们多半会笑逐颜开而再度光顾。
如上所见, route() 装饰器把一个函数绑定到对应的 URL 上。
这里是一些基本的例子:
@app.route('/')
def index():
return 'Index Page'
@app.route('/hello')
def hello():
return 'Hello World'
但是,不仅如此!你可以构造含有动态部分的 URL,也可以在一个函数上附着多个规则。
模板
怎么给服务器的用户呈现一个漂亮的页面呢?
肯定需要用到 html、css ,如果想要更炫的效果还要加入 js ,问题来了,这么一大堆字段串全都写到视图中通过 return 返回,这样定义就太麻烦了吧,因为定义字符串是不会出任何效果和错误的,如果有一个专门定义前端页面的地方就好了。
为此Flask 配备了 Jinja2 模板引擎。
创建模板
Flask 会在 templates 文件夹里寻找模板。所以,如果你的应用是个模块,这个文件夹应该与模块同级;如果它是一个包,那么这个文件夹作为包的子目录:
例如在应用下的template文件夹下创建模板index.html
接下来你可以使用 render_template() 方法来渲染模板。你需要做的一切就是将模板名和你想作为关键字的参数传入模板的变量。这里有一个展示如何渲染模板的简例:
from flask import render_template
@app.route('/index/')
def hello(name=None):
name = "张三"
return render_template('index.html', name=name)
在 index.html 中添加如下代码:
Hello from Flask{% if name %}
Hello {{ name }}!
{% else %}
Hello World!
{% endif %}
运行Flask 程序输入 127.0.0.1:5000/index 即可看到如下结果:
模板的基本语法
模板中使用变量:在 html 中定义{{ 变量名 }}
在 flask 中设定变量的key 和 value:render_template('index.html', name="张三")
使用判断语句:
{% if name %}
{% else %}
{% endif %}
使用循环语句:
{% for i in range(1,10) %}
{% for j in range(1,i+1) %}
{{ j }} x {{ i }} = {{ i*j }}
{% endfor %}
{% endfor %}
ORM
我们之前操作数据库是通过写sql语句,那么能不能不写sql语句就可以操作数据库呢? 答案是可以的。
ORM框架
O是object,也就类对象的意思,R是relation,翻译成中文是关系,也就是关系数据库中数据表的意思,M是mapping,是映射的意思。在ORM框架中,它帮我们把类和数据表进行了一个映射,可以让我们通过类和类对象就能操作它所对应的表格中的数据。ORM框架还有一个功能,它可以根据我们设计的类自动帮我们生成数据库中的表格,省去了我们自己建表的过程。
模型设计
在Flask中使用mysql数据库,需要安装一个flask-sqlalchemy的扩展。
pip install flask-sqlalchemy
要连接mysql数据库,还需要安装 flask-mysqldb
pip install flask-mysqldb
flask连接数据库
下列代码即可连接本机MySQL的test3数据库,假设用户名为root,密码为mysql
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/test3'
定义模型类进行数据库操作
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
#设置连接数据库的URL
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/Flask_test'
#设置每次请求结束后会自动提交数据库中的改动
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
#查询时会显示原始SQL语句
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
class Role(db.Model):
# 定义表名
__tablename__ = 'roles'
# 定义列对象
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
us = db.relationship('User', backref='role')
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True, index=True)
email = db.Column(db.String(64),unique=True)
pswd = db.Column(db.String(64))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
if __name__ == '__main__':
db.drop_all()
db.create_all()
ro1 = Role(name='admin')
ro2 = Role(name='user')
db.session.add_all([ro1,ro2])
db.session.commit()
us1 = User(name='wang',email='wang@163.com',pswd='123456',role_id=ro1.id)
us2 = User(name='zhang',email='zhang@189.com',pswd='201512',role_id=ro2.id)
us3 = User(name='chen',email='chen@126.com',pswd='987654',role_id=ro2.id)
us4 = User(name='zhou',email='zhou@163.com',pswd='456789',role_id=ro1.id)
db.session.add_all([us1,us2,us3,us4])
db.session.commit()
app.run(debug=True)
代码解释:
创建表:
db.create_all()
删除表:
db.drop_all()
插入一条数据:
ro1 = Role(name='admin')
db.session.add(ro1)
db.session.commit()
#再次插入一条数据
ro2 = Role(name='user')
db.session.add(ro2)
db.session.commit()
一次插入多条数据
us1 = User(name='wang',email='wang@163.com',pswd='123456',role_id=ro1.id)
us2 = User(name='zhang',email='zhang@189.com',pswd='201512',role_id=ro2.id)
us3 = User(name='chen',email='chen@126.com',pswd='987654',role_id=ro2.id)
us4 = User(name='zhou',email='zhou@163.com',pswd='456789',role_id=ro1.id)
db.session.add_all([us1,us2,us3,us4])
db.session.commit()
查询:filter_by精确查询
返回名字等于wang的所有人
User.query.filter_by(name='wang').all()
first()返回查询到的第一个对象
User.query.first()
all()返回查询到的所有对象
User.query.all()
filter模糊查询,返回名字结尾字符为g的所有数据。
User.query.filter(User.name.endswith('g')).all()
get(),参数为主键,如果主键不存在没有返回内容
User.query.get()
逻辑非,返回名字不等于wang的所有数据。
User.query.filter(User.name!='wang').all()
逻辑与,需要导入and,返回and()条件满足的所有数据。
from sqlalchemy import and_
User.query.filter(and_(User.name!='wang',User.email.endswith('163.com'))).all()
逻辑或,需要导入or_
from sqlalchemy import or_
User.query.filter(or_(User.name!='wang',User.email.endswith('163.com'))).all()
查询数据后删除
user = User.query.first()
db.session.delete(user)
db.session.commit()
User.query.all()
更新数据
user = User.query.first()
user.name = 'dong'
db.session.commit()
User.query.first()
使用update
User.query.filter_by(name='zhang').update({'name':'li'})
关联查询示例:角色和用户的关系是一对多的关系,一个角色可以有多个用户,一个用户只能属于一个角色。
查询角色的所有用户:
#查询roles表id为1的角色
ro1 = Role.query.get(1)
#查询该角色的所有用户
ro1.us
查询用户所属角色:
#查询users表id为3的用户
us1 = User.query.get(3)
#查询用户属于什么角色
us1.role
总的来说使用Flask进行数据库开发的步骤如下:配置连接数据库的选项
定义模型类
通过类和对象完成数据库增删改查操作
案例
基本知识点已经学完了,接下来完成一个示例项目:
现在还需要的代码包括三个方面,三个方面顺序不分先后。定义视图
定义模板
连接操作数据库
目标是完成城市应用的增删改查功能。
表结构如下:
| 列名 | 说明 | 类型 | | --- | --- | --- | | id | 表id(自动递增,主键) | int | | provincename | 省名 | varchar | | cityname | 省会名 | varchar | | usernumber | 用户数量 | int |
要求实现Flask对数据库的增删改查操作。
定义视图、连接操作数据库
from flask import Flask,render_template,request,redirect
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy(app)
#设置数据库连接
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:asd8283676@127.0.0.1:3306/web'
#定义模型
class City(db.Model):
#表模型
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
provincename = db.Column(db.String(255))
cityname = db.Column(db.String(255))
usernumber = db.Column(db.Integer)
#查询所有数据
@app.route("/select")
def selectAll():
cityList = City.query.order_by(City.id.desc()).all()
return render_template("index.html",city_list = cityList)
@app.route('/')
def index():
return selectAll()
#添加数据
@app.route('/insert',methods=['GET','POST'])
def insert():
#进行添加操作
province = request.form['province']
cityname = request.form['city']
number = request.form['number']
city = City(provincename=province,cityname=cityname,usernumber=number)
db.session.add(city)
db.session.commit()
#添加完成重定向至主页
return redirect('/')
@app.route("/insert_page")
def insert_page():
#跳转至添加信息页面
return render_template("insert.html")
#删除数据
@app.route("/delete",methods=['GET'])
def delete():
#操作数据库得到目标数据,before_number表示删除之前的数量,after_name表示删除之后的数量
id = request.args.get("id")
city = City.query.filter_by(id=id).first()
db.session.delete(city)
db.session.commit()
return redirect('/')
#修改操作
@app.route("/alter",methods=['GET','POST'])
def alter():
# 可以通过请求方式来改变处理该请求的具体操作
# 比如用户访问/alter页面 如果通过GET请求则返回修改页面 如果通过POST请求则使用修改操作
if request.method == 'GET':
id = request.args.get("id")
province = request.args.get("provincename")
cityname = request.args.get("cityname")
usernumber = request.args.get("usernumber")
city = City(id = id,provincename=province,cityname=cityname,usernumber = usernumber)
return render_template("alter.html",city = city)
else:
#接收参数,修改数据
id = request.form["id"]
province = request.form['province']
cityname = request.form['city']
number = request.form['number']
city = City.query.filter_by(id = id).first()
city.provincename = province
city.cityname = cityname
city.usernumber = number
db.session.commit()
return redirect('/')
if __name__ == "__main__":
app.run(debug = True,host='0.0.0.0',port=8080)
定义模板
主页:
index.html
首页province | cityname | usernumber | 操作 |
{% for item in city_list %}
{{item.provincename}}{{item.cityname}}{{item.usernumber}} 编辑{% endfor %}
修改页面
alter.html
修改修改信息
省份
autocomplete="off"
class="layui-input" value="{{city.provincename}}">
城市
class="layui-input" value="{{city.cityname}}">
人数
autocomplete="off"
class="layui-input" value="{{city.usernumber}}">
立即提交
重置
添加数据页面
insert.html
增加添加信息
省份
class="layui-input">
城市
class="layui-input">
用户数
class="layui-input">
立即提交
重置
执行项目、查看效果、测试功能
网页美化
下载LayUI
在LayUI官网 下载layui相关组件。
项目引入
将Layui相关文件放入项目的static目录下(static目录用于存放静态文件如css,js文件可以让网站看起来更加美观)。
引入LayUI需要在HTML文件中导入LayUI的CSS文件和JS模块,格式如下:
修改项目HTML文件
index.html:
首页编号省份城市名称用户数量
{% for item in city_list %}
{{item.id}}{{item.provincename}}{{item.cityname}}{{item.usernumber}}{% endfor %}
layui.use('table', function () {
var table = layui.table;
//转换静态表格 table.init('test', {
height: 1000,
width: 600//设置高度 ,limit:100
});
table.on('tool(test)', function(obj){ //注:tool 是工具条事件名,test 是 table 原始容器的属性 lay-filter="对应的值" var data = obj.data //获得当前行数据 ,layEvent = obj.event; //获得 lay-event 对应的值 if(layEvent === 'detail'){
layer.msg('查看操作');
} else if(layEvent === 'del'){
layer.confirm('真的删除行么', function(index){
location.href='/delete?id='+data.id;
});
} else if(layEvent === 'edit'){
location.href='/alter?id=' + data.id + "&provincename=" +data.provincename + "&cityname=" + data.cityname + "&usernumber=" + data.usernumber;
}
});
});
alter.html:
修改修改信息
省份
autocomplete="off"
class="layui-input" value="{{city.provincename}}">
城市
class="layui-input" value="{{city.cityname}}">
人数
autocomplete="off"
class="layui-input" value="{{city.usernumber}}">
立即提交
重置
//Demo layui.use('form', function () {
var form = layui.form;
});
insert.html:
增加添加信息
省份
class="layui-input">
城市
class="layui-input">
用户数
class="layui-input">
立即提交
重置
layui.use('form', function () {
var form = layui.form;
});
重新运行项目
可以看到页面的颜值已经比之前没加任何样式提升了很多:
将网站发布到公网
配置环境
选择一台云主机,在云主机上配置好Python环境和MySQL环境。
部署服务
使用 nohup 命令可以后台部署python服务:
nohup python app.py &
然后使用exit命令退出 linux 的连接
购买域名
在公网的机器上部署好的服务器,现在还只能通过公网访问,要想通过域名能访问网站,还需要设置域名解析。
要给 web服务配置上域名,还需要到服务商网站购买域名,例如:阿里云、腾讯云等等。
域名备案
购买了域名之后无法直接使用,需要进行域名备案,同样在服务商那里备案即可。
域名接下
备案之后的域名就可以使用了,
使用域名解析,将指定域名解析到服务器的公网ip上,就可以通过这个域名来访问web服务器。
反向代理
域名解析完成之后,还需要做的工作是反向代理。
使用 nginx 服务器可以作为域名代理服务。
通过这几个步骤就可以让你的web服务成为面向互联网的web服务啦。
可以参考的最简单 nginx 配置文件配置如下:
这个代表将 http://flask.codejiaonang.com 域名,映射到本地web服务器的8848端口。
server {
listen 80;
autoindex on;
server_name flask.codejiaonang.com;
access_log /usr/local/nginx/logs/access.log combined;
location / {
proxy_pass http://127.0.0.1:8848/;
}
}
总结
网站项目开发的基本模式
通过Flask快速入门,我们可以将网站项目开发的基本模式总结为下图:
以 Flask 项目为例,Flask作为Web框架,它的作用主要是为了开发Web应用程序。
一个Web应用程序包含了三个部分,前端,服务端,数据库。数据库负责存储数据,作为数据存储和查询的引擎;
前端网站作为用户直接查看的页面,负责展示数据。
Flask 负责对数据库进行操作,将数据库中的数据渲染至前端。
这个项目的线索
如果你也想让你的网站成为一个像那些商业级网站一样面向整个互联网的话。
可以按照下列线索,从购买服务器到购买域名,最后通过将域名与服务器和服务器中部署的网站相关联,一步一步来完成,当然其中会遇到很多困难和挑战和很多一时半会无法解决的错误,但是当你自己通过查找资料,解决问题,最终能把网站部署成功,你会发现这些知识都自然而然的融入到自己的知识体系中了。