初识Flask
初识Flask
Flask简介
Flask是一个基于Python的web框架,由奥地利开发商Armin Ronacher于2010年4月1日发布。它以灵活、“微”框架著称,其本身并不具备太多的功能,但通过丰富的第三方插件,使其在保持重量轻和简单的同时仍然可以进行高度扩展,让开发者能轻松应对现实开发中复杂的需求。
Flask vs Django
- Django功能大而全,Flask只包含基本的配置;
- Flask 比 Django 扩展性更好。
安装Flask
打开cmd窗口,执行如下指令即可安装。
pip install flask -i https://pypi.tuna.tsinghua.edu.cn/simple
执行如下指令查看是否安装成功。
pip list
第一个Flask项目(使用Pycharm)
打开PyCharm,选择New Project新建一个项目,之后点击左侧Flask,再选择项目存放路径以及Python解释器路径,点击Create即可。
创建成功后,项目结构如下。
flask_01
│ app.py:项目入口文件。
├─static:存放静态文件(js、css、image等)的文件夹,初始为空。
└─templates:存Jinjia2模板的文件夹,初始为空。
app.py初始内容如下。
from flask import Flask # 从flask包中导入Flask类
app = Flask(__name__) # 实例化Flask类
# __name__:代表当前app.py这个模块,其作用为 1.以后出现bug,可以帮助我们快速定位;2.对于寻找模板文件,有一个相对路径
@app.route('/') # 创建一个路由和视图函数的映射,/代表根路由
def hello_world(): # 当访问根路由时会执行此函数
return 'Hello World!'
if __name__ == '__main__':
app.run() # 运行项目
项目配置
debug模式
开启debug模式后,只要修改代码后保存,项目就会重新加载,无需手动重启项目,且开发时如果遇到bug,可以在浏览器上看到出错信息。
设置方法
法一
首先点击Edit Configurations。
勾选FLASK_DEBUG后,点击OK即可。
法二
在app.run() 中添加debug=True。
app.run(debug=True) # 运行项目
修改host
host即为监听的主机名,默认为127.0.0.1,修改host可以让其它电脑也能访问到本电脑的flask项目,修改步骤如下。
点击Edit Configurations。
在Additional options处填写预设的host值,然后点击OK即可。
修改port
port即为端口号,若要修改,同样在Additional options处修改即可。
URL与视图
URL格式
协议://域名:端口号/path,其中,path通过app.route()指定。
URL与视图的关系
在URL中,http协议默认端口为80,https协议默认端口为443,在实际使用中默认端口号可以不填写,下方两个URL均能正确访问到百度地址。
定义无参数的URL
敲写下方代码,程序运行后,可通过 127.0.0.1:8888/home 访问。
@app.route('/home')
def home():
return 'home'
定义有参数的URL
flask里通过<>来给path指定参数。
参数类型
-
string:字符串类型,可以接受除/以外的字符。
-
int:整型,可以接受能通过int()方法转换的字符。
-
float:浮点型,可以接受能通过float()方法转换的字符。
-
path:路径,类似string,但是中间可以添加/。
-
uuid:UUID类型,UUID是一组32位数的16进制所构成。
-
any:备选值中的任何一个(理解为枚举)。
类型一
敲写下方代码,程序运行后,可通过 127.0.0.1:8888/home/任意数字 访问。
@app.route('/home/<int:user_id>') # 类型的指定是可选的
def blog_detail(user_id):
return '您是:%s' % user_id
类型二
敲写下方代码,程序运行后,可通过 127.0.0.1:8888/book/list?page=任意数字 访问。
from flask import Flask, request # 从flask包中导入Flask类
# /book/list:返回第一页的数据
# /book/list?page=2:返回第二页的数据
@app.route('/book/list')
def book_list():
# arguments: 参数
# request.args: 类字典类型
page = request.args.get("page", default=1, type=int)
return f'您获取的是第{page}页的图书列表'
Jinjia2模板
Jinjia2是一个Python模板语言,安装Flask时,Jinjia2会自动安装,Jinjia2模板语言是不分缩进的。
模板渲染
无参数渲染
在templates创建文件index.html。
index.html内容如下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>这是首页。</h1>
</body>
</html>
app.py内容如下。
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return render_template('index.html')
if __name__ == '__main__':
app.run() # 运行项目
有参数渲染
在templates创建文件user_detail.html。
user_detail.html内容如下,Jinjia2通过{}来获取变量的值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户详情</title>
</head>
<body>
<h1>用户id是{{ user_id }}</h1>
</body>
</html>
app.py内容如下。
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/home/<user_id>')
def user_detail(user_id):
return render_template('user_detail.html', user_id=user_id)
if __name__ == '__main__':
app.run() # 运行项目
模板访问对象属性
Jinjia2访问对象属性有两种方法,例如{{ user.user_name }}和{{ user['user_name'] }}。
index.html内容如下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>{{ user.user_name }} + {{ user.user_email }}</h1>
</body>
</html>
app.py内容如下。
from flask import Flask, render_template
app = Flask(__name__)
class User:
def __init__(self, user_name, user_email):
self.user_name = user_name
self.user_email = user_email
@app.route('/')
def hello_world():
user = User("lili", '123456@qq.com')
return render_template('index.html', user=user)
if __name__ == '__main__':
app.run()
过滤器
在Python中,如果需要对某个变量进行处理,可以通过函数来实现;在模板中,则是通过过滤器来实现的。过滤器本质上也是函数,在模板中使用管道符号(|)来调用。例如有字符串类型变量name,想要获取它的长度,可以通过{{name | length}}来获取,length是Jinjia2内置的过滤器,Jinjia2会把name当做第一个参数传给length过滤器底层对应的函数。
内置过滤器
filter.html内容如下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>filter_demo</title>
</head>
<body>
名字:{{ user.user_name }}
名字长度:{{ user.user_name|length }}
</body>
</html>
app.py内容如下。
from flask import Flask, render_template
app = Flask(__name__)
class User:
def __init__(self, user_name, user_email):
self.user_name = user_name
self.user_email = user_email
@app.route('/filter')
def filte_page():
user = User('lili', '123@qq.com')
return render_template('filter.html', user=user)
if __name__ == '__main__':
app.run()
自定义过滤器
过滤器本质上是Python的函数,它会把被过滤的值当做第一个参数传给这个函数,函数经过一些逻辑处理后,再返回新的值。在过滤器函数写好后,可以通过@app.template_filter装饰器或者app.add_template_filter函数来把函数注册成Jinjia2能用的过滤器。
下方代码定义了一个date_format函数,第一个参数是需要被处理的值,第二个参数是时间的格式,并且指定了一个默认值,通过app.add_template_filter,将date_format函数注册成了过滤器,并且过滤器的名字为d_format,如果app.add_template_filter没有传第二个参数,那么默认将使用函数的名称来作为过滤器的名称。
filter.html内容如下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>filter_demo</title>
</head>
<body>
时间:{{ my_time|d_format }}
</body>
</html>
app.py内容如下。
from flask import Flask, render_template
from datetime import datetime
app = Flask(__name__)
@app.route('/filter')
def filte_page():
my_time = datetime.now()
return render_template('filter.html', my_time=my_time)
def date_format(value, format="%Y-%m-%d %H:%M"):
return value.strftime(format)
app.add_template_filter(date_format, 'd_format')
if __name__ == '__main__':
app.run()
控制语句
if语句
filter.html内容如下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>filter_demo</title>
</head>
<body>
{% if age > 18 %}
<div>您已满18岁</div>
{% elif age == 18 %}
<div>您刚满18岁</div>
{% else %}
<div>您未满18岁</div>
{% endif %}
</body>
</html>
app.py内容如下。
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/filter')
def filte_page():
age = 17
return render_template('filter.html', age=age)
if __name__ == '__main__':
app.run()
for循环
Jinjia2中的for循环没有break语句。
filter.html内容如下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>filter_demo</title>
</head>
<body>
{% for student in students %}
<div>学生姓名:{{ student.name }},学生年龄:{{ student.age }}</div>
{% endfor %}
</body>
</html>
app.py内容如下。
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/filter')
def filte_page():
students = [
{'name':'lili', 'age':18},
{'name':'lucy', 'age':17},
{'name':'lfiv', 'age':19}
]
return render_template('filter.html', students=students)
if __name__ == '__main__':
app.run() # 运行项目
模板继承
一个网站中,大部分网页的模块是重复的,比如顶部的导航栏、底部的备案信息等,如果在每个页面中都重复地去写这些代码,会让项目变得臃肿,提高后期维护成本。此时,可以通过模板继承,把一些重复性的代码写在父模板里,子模板继承父模板后,再分别实现自己的代码。
父模板文件base.html内容如下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<ul>
<li><a href="#">首页</a></li>
<li><a href="#">新闻</a></li>
</ul>
{% block body %}
{% endblock %}
<footer>底部标签</footer>
</body>
</html>
child1.html内容如下。
{% extends 'base.html' %}
{% block title %}
我是子模板
{% endblock %}
{% block body %}
我是子模板的文字
{% endblock %}
app.py内容如下。
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/child1')
def child1():
return render_template('child1.html')
if __name__ == '__main__':
app.run()
加载静态文件
static.html内容如下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>static_demo</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<script src="{{ url_for('static', filename='js/my.js') }}"></script>
</head>
<body>
<img src="{{ url_for('static', filename='images/air.jpg') }}" alt="">
</body>
</html>
app.py内容如下。
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/static')
def static_demo():
return render_template('static.html')
if __name__ == '__main__':
app.run()
操作MySQL
Python操作MySQL驱动
Flask要想操作数据库,必须要先安装Python操作MySQL的驱动,在Python中,目前有以下MySQL驱动包:
- MySQL-python:也就是MySQLdb,是对C语言操作MySQL数据库的一个简单封装,但只支持Python2。
- mysqlclient:是MySQL-python的一个分支,支持Python3并且修复了一些bug,是目前为止执行效率最高的驱动,但是安装的时候容易因为环境问题出错。
- pymysql:纯Python实现的驱动,执行效率不如mysqlclient,可以和Python代码无缝衔接。
- mysql-connector-python:MySQL官方推出的纯Python连接MySQL驱动,执行效率比pymysql慢。
安装pymysql
pip install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple
安装Flask-SQLAlchemy
在Flask中,很少会使用pymysql直接写原生SQL语句去操作数据库,更多的是通过SQLAlchemy提供的ORM技术,其类似于操作普通Python对象那样来实现对数据库的增删改查,而Flask-SQLAlchemy是对SQLAlchemy的一个封装,使得在Flask中使用SQLAlchemy更加方便。Flask-SQLAlchemy需要单独安装,因为Flask-SQLAlchemy依赖SQLAlchemy,所以只要安装了Flask-SQLAlchemy,SQLAlchemy会自动安装。
pip install flask-sqlalchemy -i https://pypi.tuna.tsinghua.edu.cn/simple
SQLAlchemy类似于Jinjia2,是可以独立于Flask而被使用的,完全可以在任何Python程序中被使用。SQLAlchemy官方链接
Flask-SQLAlchemy基本使用
连接MySQL
使用Flask-SQLAlchemy操作数据库之前,需要先创建一个由FLask-SQLAlchemy提供的SQLAlchemy类的对象,在创建这个类的时候,需要传入当前的app,然后要在app.config中设置SQLALCHEMY_DATABASE_URI,来配置数据库的连接。
app.py内容如下。
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
app = Flask(__name__)
HOSTNAME = '127.0.0.1' # 主机名
PORT = 3306 # 端口号
USERNAME = 'root' # 连接MySQL的用户名
PASSWORD = '123456' # 连接MySQL的密码
DATABASE = 'db_flask_test' # 在MySQL创建的数据库名称
app.config['SQLALCHEMY_DATABASE_URI'] = f'mysql+pymysql://{USERNAME}: {PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4'
db = SQLAlchemy(app)
# 在app.config中设置好连接的数据库信息,然后使用SQLAlchemy(app)创建一个db对象,QLAlchemy会自动读取app.config中连接数据库的信息
# 测试是否连接成功
with app.app_context(): //应用上下文
with db.engine.connect() as conn:
rs = conn.execute(text("select 1"))
print(rs.fetchone()) //若打印1,则说明连接成功
@app.route('/')
def hello_world():
return render_template('index.html')
if __name__ == '__main__':
app.run() # 运行项目
注:测试时若报错,提示语句不是可执行的对象,可以使用sqlalchemy.text() 函数对语句进行封装。
ORM模型与表的映射
ORM模型
对象关系映射(Object Relationship Mapping),简称ORM,是一种可以用Python面向对象的方式来操作关系型数据库的技术,具有可以映射到数据库表能力的Python类我们称之为ORM模型。一个ORM模型与数据库中一个表相对应,ORM模型中的每个类属性分别对应表的每个字段,ORM模型的每个实例对象对应表中每条记录。ORM技术提供了面向对象的SQL交互的桥梁,让开发者用面向对象的方式操作数据库,使用ORM模型具有以下优势:
- 开发效率高:几乎不需要写原生SQL语句,使用纯Python的方式操作数据库。
- 安全性高:ORM模型底层代码对一些常见的安全问题,比如SQL注入做了防护,比直接使用SQL语句更加安全。
- 灵活性强:Flask-SQLAlchemy底层支持SQLite、MySQL、Oracle、PostgreSQL等关系型数据库,但针对不同数据库,ORM模型代码几乎一模一样,只需要修改少量代码,即可完成底层数据库的更换。
映射方法
法一——通过db.create_all()映射。
with app.app_context():
db.create_all() # 将所有的表同步到数据库中
法二——通过flask-migrate映射。
db.create_all()只能识别新增了哪些模型,把新增的模型同步到数据库中,若是模型中字段值改变,它无法识别,这种情况下需要使用flask-migrate。
安装flask-migrate代码如下。
pip install flask-migrate -i https://pypi.tuna.tsinghua.edu.cn/simple
flask-migrate三部曲,在命令行中执行如下指令。
- flask db init:只执行一次,执行后生成migrations文件夹。
- flask db migrate:识别ORM模型的改变,生成迁移脚本。
- flask db upgrade:运行迁移脚本,同步到数据库中。
MySQL的增删改查
使用ORM进行增删改查操作,需要先把操作添加到会话中,通过db.session可以获取到会话对象,会话对象只是在内存中,如果要把会话中的操作提交到数据库中,需要调用db.session.commit()操作,如果想要把会话中的操作回滚,则可以通过db.session.rollback()实现。
增加数据
使用ORM创建一条数据,首先需使用ORM模型创建一个对象,然后添加到会话中,再进行commit操作即可。创建对象时,必须要通过关键字参数给字段赋值。
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(100), nullable=False)
password = db.Column(db.String(100), nullable=False)
@app.route("/user/add")
def add_user():
# 1. 创建ORM对象
user = User(username="张三", password="123456")
# sql: insert into user(username, password) values('张三', '123456')
# 2. 将ORM对象添加到db.session中
db.session.add(user)
# 3. 将db.session中的改变同步到数据库中
db.session.commit()
return '用户添加成功'
查找数据
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(100), nullable=False)
password = db.Column(db.String(100), nullable=False)
@app.route("/user/query")
def query_user():
# 1. get查找:根据主键查找
user = User.query.get(1)
print(f'{user.id}-{user.username}-{user.password}')
# 2. filter_by查找,返回Query数组
users = User.query.filter_by(username='张三')
for user in users:
print(user.username)
return '数据查找成功!'
修改数据
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(100), nullable=False)
password = db.Column(db.String(100), nullable=False)
@app.route("/user/update")
def update_user():
user = User.query.filter_by(username='张三').first()
user.password = '654321'
db.session.commit()
return '数据修改成功'
删除数据
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(100), nullable=False)
password = db.Column(db.String(100), nullable=False)
@app.route('/user/delete')
def delete_user():
# 1. 查找
user = User.query.get(1)
# 2. 从db.session中删除
db.session.delete(user)
# 3. 将db.session中的修改同步到数据库中
db.session.commit()
return '数据删除成功'
ORM模型外键与表的关系
关系型数据库中,多个表之间可以建立关系,表关系总体上可以分成三种,分别是:一对一、一对多(多对一)、多对多,表关系的建立是通过数据库层面的外键来实现的,创建外键是通过db.ForeignKey实现的。
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(100), nullable=False)
password = db.Column(db.String(100), nullable=False)
articles = db.relationship('Article', back_populates='author')
class Article(db.Model):
__tablename__ = 'article'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
author = db.relationship('User', back_populates='articles')
# author = db.relationship('User', backref='articles'), backref会自动给User模型添加一个article属性,用来获取文章列表
@app.route("/article/add")
def article_add():
article1 = Article(title='Flask学习', content="xxxxx")
article1.author = User.query.get(2)
article2 = Article(title='Django学习', content='xxxxx')
article2.author = User.query.get(2)
# 添加到session中
db.session.add_all([article1, article2])
# 同步session中的数据到数据库中
db.session.commit()
return '文章添加成功'
@app.route('/article/query')
def query_article():
user = User.query.get(2)
for article in user.articles:
print(article.title)
return '文章查找成功'