Bootstrap

3600字带你入门Python框架Flask

Flask 框架

1.Flask介绍

flask 是一门使用 python 编写的后端框架。

image-20230414135633255

另外一门非常优秀的 Django 框架:

image-20230414135617929

目前是建议是初学者使用 flask 进行开发。

因为 Django 有着一系列复杂的我们并不需要使用的功能。

python环境

image-20230414135929572

如果没有 python 的环境的话,请安装最新的 python 环境即可。

flask安装

pip install Flask

记得把天杀的梯子关了。

image-20230414140704390

开发工具pychram

请到官网下载。(专业版最好

实在要用 vscode 也一样。

2.第一个项目

项目目录

image-20230414143831764

# 从 Flask 这个包中导入 flask 这个类
from flask import Flask

# 使用Flask类创建一个app对象
# __name__ 代表当前的这个模块
# 1.以后出现bug,它可以帮助我们快速定位
# 2.对于寻找模板文件,有一个相对路径
app = Flask(__name__)

# 创建一个路由和视图函数的映射
# 创建的是根路由
# 访问根路由,就会执行hello_world这个函数
@app.route('/')
def hello_world():  # put application's code here
    return 'Hello World!'

# 运行代码
if __name__ == '__main__':
    app.run()

执行:

pychram 直接点击右上角直接运行;

vscode 可以在终端中输入 flask run 也可以运行。

image-20230414144420822

3.项目配置

debug模式

默认是没有开启的。

image-20230414144647644

如果没有开启 debug 模式的话,你修改完代码后必须将项目停止,然后重启才能够有效果。(我们希望的效果当然是直接保存然后就有效果)

所有一般我们打开 debug 模式。

如何开启:image-20230414144947235

image-20230414144932169

不仅如此,开启了 debug 模式之后,如果报错了,我们在浏览器上就可以看到报错信息。

此外:也可以在代码中直接修改

if __name__ == '__main__':
    app.run(debug=True)

修改host

默认情况下是 localhost:

image-20230414145234610

但是外部是不能访问的。

如果想让在同一个网络下的其他设备可以访问的话,需要修改为0.0.0.0

然后我们就可以使用ip地址进行访问。

如何修改host:

image-20230414145533194
在这里插入图片描述

image-20230414145706208

或者也可以使用 --host=你的ipaddress也行。

效果:

image-20230414145831305

修改端口号

就是修改项目运行的端口号。

image-20230414145945135

还是在刚刚修改host的地址。

4.url与视图

url的组成部分:

协议 + 域名 + 端口号(默认是80 \ 443) + 路由

我们使用 @app.route()即可定义路由:

@app.route('/')
def hello_world():  # put application's code here
    return '你好china'

@app.route('/my')
def my():  # put application's code here
    return '这里是李嘉俊'

也可以定义多层。

url携带参数

@app.route('/my/blog/<blog_id>')
def blog_detail(blog_id):  # put application's code here
    return '您访问的博客是{}'.format(blog_id)

参数携带类型

如果携带类型的话,能够强制转换的话会被强制转换。

image-20230414151733625

可以定义的类型:

image-20230414151754338

默认参数

/book/list
/book/list?page=2

使用 request

from flask import Flask,request
@app.route('/book/list')
def book_detail():  # put application's code here
    page = request.args.get('page', default=1, type=int)
    return '你获取的是{}'.format(page)

请求类型

post 和 get …

5.模板渲染

模板引擎:Jinja2

虽然 jinja2 是一个单独的库,但是由于 flask 依赖了jinja2,所以不必单独安装。

引入:

from flask import render_template

使用:

@app.route('/template')
def template():  # put application's code here
    // 渲染
    return render_template('demo.html')

效果:

image-20230414190231595

image-20230414190213756

动态渲染

还是使用 jinja2 的 render_template

@app.route('/template/blog/<blog_id>')
def template(blog_id):  # put application's code here
    return render_template('demo.html', blog_id=blog_id)
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <a href="www.baidu.com">百度</a>
    <div class="header">这里是博客{{ blog_id }}</div>
  </body>
</html>

效果:

image-20230414190853764

模板访问对象属性

一样的:

@app.route('/template/blog/<blog_id>')
def template(blog_id):  # put application's code here
    uesr = User('lijiajun', '[email protected]')
    person = {
        'username': 'zhangsan',
        'email': 'zhangsan的email'
    }
    return render_template('demo.html', blog_id=blog_id, user=uesr, person=person)
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <a href="www.baidu.com">百度</a>
    <div class="header">这里是博客{{ blog_id }}</div>
    <div>{{ user }}</div>
    <div>{{ user.username }}</div>
    <div>{{ user.email }}</div>
    <div>{{ person.username }}</div>
    <div>{{ user.email }}</div>
  </body>
</html>

结果:

image-20230414192039502

6.jinja2过滤器

使用过滤器

使用管道符号 | 即可。

就是处理从render_template中传入的一些数据,可以在 template 中使用 jinja2 的过滤器。

比如求传入的长度:

  <div>{{ user.email | length }}</div>

image-20230414192757471

有几十个内置的过滤器,查看 jinja2 的文档即可。

自定义过滤器

自定义相关函数,然后增加即可。

# 自定义过滤器
def filter(value):
    return value.upper()


# 增加过滤器
app.add_template_filter(filter, 'filter')
<div>{{ user.email | filter }}</div>

效果:

image-20230414193618855

7.jinja2控制语句

if语句

<div>
    {% if age > 18 %}可以进入 {% endif %} {% if age < 18 %} 禁止进入{% endif %}
</div>

效果:

image-20230414194748082

for循环

注意没有 break 这样的语句。

<div>
    {% for foo in age %}

    {% endfor %}

</div>

8.jinja2模板继承

封装组件

就是组件,在这里称为需要复用的模板。

举例:我们新建一个组件:

image-20230414195626738

在别的页面中使用:

{% extends 'component.html' %}

image-20230414195524944

组件传参

demo.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>test</title>
  </head>
  <body>
    <div>
      {% extends 'component.html' %} {% block content %} 我是传过来的内容 {%
      endblock %} {% block title %} 我是传过来的标题 {% endblock %}
    </div>
  </body>
</html>

component.html

在其中留空即可。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>这是一个组件</title>
  </head>
  <body>
    这是一个组件 {% block title %}{% endblock %} {% block content %}{% endblock
    %}
  </body>
</html>

效果:

image-20230414200047018

9.jinja2中加载静态文件

加载图片

<body>
    <div>
        <img src='{{ url_for("static",filename="image.png") }}' alt="" />
    </div>
</body>

结果:

image-20230414200826203

加载css文件

<head>
    <meta charset="UTF-8" />
    <title>test</title>
    <link rel="stylesheet" href='{{ url_for("static",filename="demo.css") }}' />
</head>

效果:

image-20230414201146694

加载js文件

一样的:

<script src='{{ url_for("static",filename="demo.js")}}'></script>

总结

都是使用 {{ url_for('static',filename="demo")}} 就可以了。

10.操作数据库(Mysql)

安装相关包

  • MySQL-python:老旧的包,只支持 py2
  • mysqlclient:对上面这个包的改善,支持了 py3,是目前的驱动包中效率(纯c语言编写)最高的,但安装的时候环境容易出问题
  • pymysql:纯 py 编写,效率比较低,但是正因为如此,它可以和 python 无缝衔接
  • mysql-connector-python:纯 py 写的,比上面这个还慢

为了减少出错:可以使用 pymysql

pip install pymysql

我们不会直接使用这个包去操作数据库,因为需要写原生的SQL语句,我们最好使用 orm。

所以我们还需要下一个:

pip install flask-sqlalchemy

sqlalchemy 给我们提供了 orm 的技术,可以让我们像操作普通对象一样去操作数据库。

这个需要额外安装,因为 flask 没有这个依赖。

连接数据库

  • 在 app.config 中设置好连接数据库的相关信息
  • 然后使用 SQLAlchemy(app)创建一个 db 对象
  • SQLAlchemy 会自动读取 app.config 中的连接信息
# 从 Flask 这个包中导入 flask 这个类
from flask import Flask, request, render_template
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text

app = Flask(__name__)
# 配置文件
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'admin123'
DATABASE = 'my_db_01'
print('mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8mb4'.format(USERNAME, PASSWORD,
                                                              HOSTNAME, PORT,
                                                              DATABASE))
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8mb4'.format(USERNAME, PASSWORD,
                                                                                                HOSTNAME, PORT,
                                                                                                DATABASE)
db = SQLAlchemy(app)

with app.app_context():
    with db.engine.connect() as conn:
        rs = conn.execute(text("select 1"))
        print(rs.fetchone())

结果:

image-20230414211012119

orm模型与表的映射

orm(object relationship mapping) : 对象关系映射,是一种可以利用 python 面向对象的方式来操作关系型数据库的技术。

优点:

  • 开发效率高,不需要写 SQL 语句
  • 安全性强:对常见的 SQL 问题进行了处理,比如 SQL 注入等等
  • 灵活性强:flask-sqlalchemy的orm支持一系列关系型数据库,只需要在连接的时候正确配置即可

写法:

# User orm
class User(db.Model):
    # 映射表名
    __tablename__ = 'user'
    # 配置数据库元素
    # id,username,password 的各自的属性(主键,类型,是否可以为空,自增等等)
    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)

# 下面的语句实际上就是映射为 insert 语句
user = User(username='法外狂徒张三',password='111111')

# 将你创建的表推到数据库
with app.app_context():
    db.create_all()

如果数据库中已经有相关的表的话,也就是说我们不需要在代码中新建表,我们直接读取数据库中的表:

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

my_table = db.Table('my_table',
                    db.Column('id', db.Integer, primary_key=True),
                    db.Column('name', db.String(50)),
                    db.Column('age', db.Integer))

orm与crud操作

create
@app.route('/user/add')
def addUser():
    user = User(username='法外狂徒张三', password='111111')
    db.session.add(user)
    db.session.commit()
    return '新建用户成功'
read
@app.route('/user/read')
def readUser():
    # get 查找:根据主键查找
    user = User.query.get(1)
    return '查找用户成功{}{}'.format(user.id, user.username)
@app.route('/user/read')
def readUser():
    # 1.get 查找:根据主键查找
    # user = User.query.get(1)
    # 2.使用 filter_by 查找
    users = User.query.filter_by(username='法外狂徒张三')
    print('this is users', users[0].username)
    return '查找用户成功'
update
@app.route('/user/update')
def updateUser():
    user = User.query.filter_by(username='法外狂徒张三').first()
    user.username = '法外狂徒李嘉俊'
    db.session.commit()
    return '修改用户成功'
delete
@app.route('/user/delete')
def deleteUser():
    # 查找
    user = User.query.filter_by(username='法外狂徒李嘉俊').first()
    # 从 db.session 中删除
    db.session.delete(user)
    # 提交删除
    db.session.commit()
    return '删除用户成功'

orm外键与表关系

了解一下就行。

关于外键:

  • 在实际生产中基本不用
    • 因为本质上是一种耦合的体现
    • 数据表之间的查询效率不高
    • 如果有分布式的情况存在就更无法使用外键
  • 代替
    • 使用另外一张表代替,就像多对多一样
    • 使用外键但不建立外键的约束

orm映射到数据库

我们之前使用这样的方式来将模型映射到数据库:

# 将你创建的表推到数据库
with app.app_context():
    db.create_all()

但是如果模型中的字段改变了,就会出现问题。

比如:

原来我们的模型:

# user 模型
class User(db.Model):
    # 映射表名 -> 就是数据库中表名
    __tablename = 'user'
    # 配置数据库元素
    # id,username,password 的各自的属性(主键,类型,是否可以为空,自增等等)
    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)

我们这时候添加一个 email

# user 模型
class User(db.Model):
    # 映射表名 -> 就是数据库中表名
    __tablename = 'user'
    # 配置数据库元素
    # id,username,password 的各自的属性(主键,类型,是否可以为空,自增等等)
    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)
    email = db.Column(db.String(50), nullable=False)

但是数据库中并不会增加 email 字段。

image-20230416143817442

甚至你如果想修改原来的字段的一些信息:

image-20230416144205700

都是会无法修改的。

所以实际开发中我们并不会使用 db.create_all()

这时候我们需要一个第三方的包,叫做 flask-migrate

pip install flask-migrate

引入:

from flask_migrate import Migrate

初始化:

migrate = Migrate(app,db)

映射:

  • flask db init 只需要执行一次

    image-20230416144702731

  • flask db migrate 识别 orm 模型的改变,生成迁移脚本

    注意原来这个 version 中的没有文件

    image-20230416144830982

    当我们修改过模型之后,可以执行代码,然后:

    image-20230416144958892

  • flask db upgrade 运行迁移脚本,同步到数据库中

    image-20230416145256473

    可以看出,数据库中的字段已经发生了改变,同时生成了一张用于记录 version 的表:

    (存储的当前最新的 version)

    image-20230416145349924

千万注意,使用这三步走,会重置整个数据库,比如你原来数据库中有除了本项目代码以外的数据模型,会被删除。千万注意!

但是我感觉这种方法也不是很方便,不知道会不会有更简单的方法。

11.蓝图blueprint

学习自https://zhuanlan.zhihu.com/p/357444025

蓝图结构

我们在写项目的时候,肯定是不能够将所有的接口都写在同一个页面中的。

image-20230416205815305

如果接口很少的话,我们甚至只需要使用一个页面,就是 app.py 就可以解决后台的开发,但是实际上的开发往往会比较复杂,这时候我们就需要将代码模块化。

这时候我们就需要使用蓝图。

蓝图技术可以帮助我们实现 flask 应用的模块划分。

blue-print/
├── admin
│   ├── __init__.py
│   └── views.py
├── app.py
└── user
    ├── __init__.py
    └── views.py

接下来请看各个部分的代码:

app.py

from falsk import Flask
app = Flask(__name__)

// 引入
from admin import admin_blue
from user import user_blue

@app.route('/')
def hello():
    return 'hello'

app.register_blurprint(admin_blue)
app.register_blurprint(user_blue)

if(__name__ == '__main__'):
    app.run(port=5001)

user init 模块

from flask import Blueprint

# 用户部分接口的统一头
user_blue = Blueprint('user',__name__,url.prefix='/user')
from . import views

user views 模块

from user import user_blue

# 用户部分的接口
@user_blue.route('/register')
def register():
    return 'register'

@user_blue.route('/login')
def login():
    return 'login'

@user_blue.route('/modify_password')
def modify_password():
    return 'modify_password'

admin init 模块

from flask import Blueprint

# 用户部分接口的统一头
admin_blue = Blueprint('admin',__name__,url.prefix='/admin')
from . import views

admin views 模块

from admin import admin_blue

# 用户部分的接口
@admin_blue.route('/alluser')
def register():
    return 'alluser'

@admin_blue.route('/deluser')
def login():
    return 'deluser'

如上,这样的划分的话,就会好很多。

关于代码组织形式

蓝图在组织代码的时候,一共有两种组织形式

  • 功能性架构
  • 分区式架构

我们上面所展示的就是功能性架构,很明显,我们就是按照功能模块来组织的蓝图,他们共用着相同的静态资源,一起放在 static 中

一个功能性架构的目录示意图:

__init__.py
static/
templates/
    home/
    control_panel/
    admin/
views/
    __init__.py
    home.py
    control_panel.py
    admin.py
models.py

关于分区式架构:

适用于子模块有特殊需要的情况下,在创建蓝图对象时,可以指定 static 和 templates ,结构大致如下:

yourapp/
    __init__.py
    admin/
        __init__.py
        views.py
        static/
        templates/
    home/
        __init__.py
        views.py
        static/
        templates/
    control_panel/
        __init__.py
        views.py
        static/
        templates/
    models.py
;