以学习代码为主,笔记在代码块的注释部分。
1 基础语法
# 从flask这个包中导入Flask类
from flask import Flask
# 使用Flask类创建一个app对象
# __name__: 代表当前app.py这个模块
app = Flask(__name__)
# 创建一个路由和视图函数的映射
# '/'是根路由
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
2 URL与视图的映射
2.1 无参的url
# route是一个函数 一个装饰器 后面可以用来定义url里的path
# 这种是无参的url
@app.route('/')
def hello_world():
return 'Hello World!'
2.2 含参的url
2.2.1 路径传参
# 带参数的url:将参数固定到了url中, 方式是路径传参
# 可以定义参数的类型 格式:<参数类型:参数名>
@app.route("/blog/<blog_id>")
def blog_detail(blog_id):
return "您访问的博客是: %s" % blog_id
2.2.2 以查询字符串的方式传参
# 以查询字符串的方式传参,就不用在route那里写参数
# /book/list:返回第一页的数据
# /book/list?page=2:返回第二页的数据(用的是英文的?)
@app.route('/book/list')
def book_list():
# arguments: 参数
# request.args:类字典类型,可通过get取值,request是一个全局对象
# get参数:key表示要查找的键,default表示默认值,type用来指定类型
page = request.args.get("page", default=1, type=int)
# 用f字符串,变量用花括号括住{}
return f"您获取的是第{page}页图书列表"
3 Jinja2
3.1 模板渲染
# render_template是flask的一个函数,用于渲染模板
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
# render_template里的参数就是你所要渲染的模板文件,
# 默认从当前项目的template文件夹下寻找所写入的文件,然后读取并进行解析,再渲染成html代码,返回给浏览器。
return render_template("index.html")
3.2 渲染变量
后端代码:
@app.route('/')
def hello_world():
user = User(username="abc", email="xx@qq.com")
# 此处person字典类型
person = {
"username": "abc",
"email": "xx@qq.com"
}
return render_template("index.html", person=person)
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--用“对象.属性名”的格式可以只引用对象的某个属性-->
<!--引用字典对象里的某个属性用中括号,也可以用“对象.属性名”的方式(常用)引用-->
<div>{{ person['username'] }} / {{ person.email }}</div>
</body>
</html>
3.3 过滤器
3.3.1 Jinja内置过滤器
后端代码:
# 类对象,有多个属性
class User:
def __init__(self, username, email):
self.username = username
self.email = email
# 通过过滤器对某个变量(需要被过滤的值)进行处理
@app.route('/filter')
def filter_demo():
user = User(username="abc", email="xx@qq.com")
return render_template("filter.html", user=user)
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>过滤器使用demo</title>
</head>
<body>
<!--用管道符号“|”来调用过滤器,对变量user进行处理,变量user即为定义过滤器时的“value”(此处length为jinja2的内置过滤器,返回value序列的长度)-->
<div>{{ user.username }}--{{ user.username|length }}</div>
</body>
</html>
3.3.2 自定义过滤器
后端代码:
from flask import Flask, render_template
from datetime import datetime
app = Flask(__name__)
# 自定义过滤器
# 首先定义一个函数,参数包括被过滤的值value和设定的格式format
# 然后调用该函数,则会返回格式化的时间
#(time.strftime是一个python函数,用于格式化时间,格式取决于参数format)
def datetime_format(value, format="%Y-%m-%d %H:%M"):
return value.strftime(format)
# 利用函数add_template_filter()将函数定义为过滤器并命名
app.add_template_filter(datetime_format, "dformat")
@app.route('/filter')
def filter_demo():
mytime = datetime.now()
return render_template("filter.html", mytime=mytime)
if __name__ == '__main__':
app.run()
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>过滤器使用demo</title>
</head>
<body>
<div>{{ mytime|dformat }}</div>
</body>
</html>
3.4 模板继承
网站中,会有许多网页的模块是重复的,比如顶部的导航栏。通过模板继承,可以把一些重复性的代码写在父模板中,子模板继承父模板后,再分别实现对应页面的代码。
父模板:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title> <!--子模版中需要重写的部分定义为block-->
</head>
<body>
<h1>父模板</h1>
<ul>
<li><a href="#">首页</a></li>
<li><a href="#">新闻</a></li>
</ul>
{% block body %}
{% endblock %}
<footer>这是底部的标签</footer>
</body>
</html>
子模版:
<!--子模版继承父模板的代码,因此不需要其他多余的,只需填写block的内容-->
<!--与父模板不同的地方,就使用block语法,先在父模板写好一个block,再在子模版填充block-->
{% extends "base.html" %} <!--extends语法加载父模板-->
{% block title %}
我是子模版的标题
{% endblock %}
{% block body %}
我是子模版的body
{% endblock %}
3.5 控制语句
模板中的控制语句与python中的控制语句类似,注意Jinja2的格式要求。
后端代码:
@app.route('/control')
def control_statement():
age = 18
books = [{
"name": "沉默的大多数",
"author": "王小波"
}, {
"name": "动物园",
"author": "乙一"
}, ]
return render_template("control.html", age=age, books=books)
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>控制语句Demo</title>
</head>
<body>
<!--控制语句应放在一对{% %}中,且在控制语句结束后,应有相应的结束语句,即endif;for循环语句同理-->
{% if age>18 %}
<div>您已满18岁!</div>
{% elif age==18 %}
<div>您刚满18岁!</div>
{% else %}
<div>您未满18岁!</div>
{% endif %}
<!--jinja2的for循环没有break语句-->
{% for book in books %}
<div>图书名称:{{ book.name }}, 作者:{{ book.author }}</div>
{% endfor %}
</body>
</html>
3.6 加载静态文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<script src="{{ url_for('static', filename='my.js') }}"></script>
</head>
<body>
<!--加载静态文件需要用url_for()函数,在img的路径src中不能直接写这个函数,会被当成字符串,要用双花括号引起来-->
<!--用url_for()函数调用静态文件,第一个参数都是static,第二个参数就是文件名,
如果是在static文件夹下的其他文件夹,那么还要写上前面的路径(其他文件夹名/文件名)-->
<img src="{{ url_for('static', filename = 'haechan.jpg' ) }}" alt="">
</body>
</html>
4 数据库
4.1 Flask连接MySQL数据库
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# MySQL所在的主机名
HOSTNAME = "127.0.0.1"
# MySQL监听的端口号,默认3306
PORT = 3306
# 连接MySQL的用户名
USERNAME = "root"
# 连接MySQL的密码
PASSWORD = "*******"
# MySQL上创建的数据库名称
DATABASE = "database_learn"
# 在app.config中设置好连接数据库的信息
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
# 然后使用SQLAlchemy(app)创建一个db对象
# SQLAlchemy会自动读取app.config中连接数据库的信息
db = SQLAlchemy(app)
4.2 ORM模型与表的映射
ORM模型映射成表的三部曲,有什么改动之和同步到数据库时就执行以下命令:
1.flask db init(这一步只需要执行一次)
2.flask db migrate(识别ORM模型的改变,生成迁移脚本)
3.flask db upgrade(运行迁移脚本,同步到数据库中)
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app = Flask(__name__)
HOSTNAME = "127.0.0.1"
PORT = 3306
USERNAME = "root"
PASSWORD = "*******"
DATABASE = "database_learn"
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
db = SQLAlchemy(app)
migrate = Migrate(app, db)
# 以下为创建一个ORM模型,(db.Model)一定要写
class User(db.Model):
__tablename__ = "user"
# db.Column()把id映射到表中,Integer表示整型,primary_key表示是表的主键,autoincrement表示每新加一条数据,id会自动加一
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
# String表示是字符串类型,在数据库中会映射成varchar(变动的字符类型)类型,参数100表示指定的最大长度是100
# nullable=False表示字段不能为空
username = db.Column(db.String(100), nullable=False)
password = db.Column(db.String(100), nullable=False)
email = db.Column(db.String(100))
# 创建一个User类的对象,相当于创建了一条数据
user1 = User(username="abc", password="1234")
# db.create_all()表示将表同步到数据库中,即ORM模型与表的映射,但局限性大,不好用
with app.app_context():
db.create_all()
4.3 ORM模型的添加、查询、修改和删除操作
4.3.1 添加
# (表数据的)添加操作
@app.route('/user/add')
def add_user():
# 1.创建ORM对象
user = User(username='abc', password="1234")
# 2.将ORM对象添加到db.session中
db.session.add(user)
# 3.将db.session中的改变同步到数据库中
db.session.commit()
return "用户创建成功!"
4.3.2 查询
# 查询操作
@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查找,可查找多条数据
# 此处的users是一个Query,类数组对象,可进行相似的操作
users = User.query.filter_by(username="abc")
print(type(users))
for user in users:
print(user.username)
return "数据查找成功!"
4.3.3 修改
# 修改操作
@app.route('/user/update')
def update_user():
# 1.先查找到某条数据才能进行修改
user = User.query.filter_by(username="abc").first()
user.password = "123456"
# 2.同步到数据库中
db.session.commit()
return "密码修改成功!"
4.3.4 删除
# 删除操作
@app.route('/user/delete')
def delete_user():
# 1.同样需要先查找到数据再进行删除操作
user = User.query.get(1)
# 2.从db.session中删除
db.session.delete(user)
# 3.同步到数据库中
db.session.commit()
return "数据删除成功!"
4.4 ORM外键与表的关系
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)
# 添加外键“作者”,类型与所引用的外键的id的类型一致,引用的是表“user”中的字段“id”
author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
# 通过db.relationship()将author属性与User模型建立联系,
# 当访问author属性时,会自动根据表article中表user产生的外键,在表User中查找对应id的信息,再赋值给author
# backref会自动给User模型添加一个articles的属性,用来获取文章列表
author = db.relationship("User", backref="articles")
@app.route('/article/add')
def article_add():
article1 = Article(title="Flask学习", content="Flaskxxxxxxxx")
article1.author = User.query.get(2)
article2 = Article(title="Flask学习加油", content="Flaskxxxxxxxxxxxxx")
article2.author = User.query.get(2)
# 添加到session中,添加多条使用add_all
db.session.add_all([article1, article2])
# 同步到数据库中
db.session.commit()
return "文章添加成功!"
# 根据作者查找文章
@app.route('/article/query')
def query_article():
user2 = User.query.get(2)
for article in user2.articles:
print(article.title)
return "文章查找成功!"