目录:

  一、Python 现阶段三大主流Web框架 Django Tornado Flask 对比

  二、Flask 的安装与基本使用

  三、Flask 中的 Render Redirect HttpResponse

  四、Flask 中的 request 

  五、Flask 中的模板语言 Jinja2 及 render_template 的深度用法

  六、Flask 中内置的 Session

  七、Flask 中的路由系统

  八、实例化 Flask 的参数 及 对app的配置

  九、Flask 中的蓝图(BluePrint)

  十、Flask 中的 before_request after_request errorhandler

  十一、Flask 中的 CBV 模式

 

Django
    Model ORM  Session
    ModelForm 
    amdin
大而全
浪费资源

Flask
    Session
小而精,三方组件全
稳定性相对较差

Tornado
异步IO 非阻塞 原生websocket
干净

Sanic   HOT





from flask import Flask
app = Flask(__name__)
app.run()


from flask import Flask
app = Flask(__name__)

@app.route("/")
def index():
    return "Hello World"

app.run()




Response三剑客:
HttpResponse: return "Hello World" 返回字符串至客户端

from flask import render_template
render : return render_template("login.html")
与Django中的 render 使用一致 返回模板由浏览器渲染

from flask import redirect
redirect: return redirect("/login") # 302
跳转,重定向URL

Flask中小儿子:
1. from flask import jsonify
   return jsonify({name:111}) # 返回json标准的字符串 
Content-Type:application/json

2. from flask import send_file
   return send_file(path)
# 打开文件并返回文件内容(自动识别文件格式)




falsk 中的 request 
get post delete put

django
def django(req):
    return HttpResponse("hello")
    
    
from flask import request
request.method # 请求方式
request.form # 存放FormData中的数据 to_dict 序列化成字典
request.args # 获取URL中的数据 to_dict 序列化成字典
request.url # 访问的完整路径
request.path # 路由地址
request.host # 主机地址
request.values # 获取 FormData and URL中的数据 不要用to_dict
request.json  # 如果提交时请求头中的Content-Type:application/json 字典操作
request.data  # 如果提交时请求头中的Content-Type 无法被识别 将请求体中的原始数据存放 byte
request.cookies # 获取Cookie中的数据
request.headers # 获取请求头
request.files # 序列化文件存储 save('文件名')




Jinja2
{{ }} 引用变量 执行函数
{% %} 逻辑代码
安全标签字符串前端用|safe 后端用Markup  
@app.template_global()
@app.template_filter()
{% macro create_input(na,ty) %}
{{ na }} : <input type="{{ ty }}" name="{{ na }}">
{% endmacro %}
{{ create_input("username","text") }}





Flask 中的 Session

app.secret_key = "加密字符串" # 用于序列化和反序列化 session信息

由于Flask中默认Session 存放位置 - 客户端的Cookies中
所以Session需要加密 用到 secret_key
请求进入视图函数 带上cookie 将Session从cookie序列化出来 通过secret_key 反序列化成字典
Flask-Session

@app.route("/")
def index():
    if not session.get("user"):
        return redirect("/login")

    return render_template("index.html")





装饰器坑
    endpoint 默认视图函数名
    import functools
    @functools.wraps(装饰函数)
    




flask中的路由
    1.endpoint 反向生成url地址标志 默认视图函数名 url_for
    2.methods 视图函数允许的请求方式
    3."/index/<page>" 动态路由参数
        def index(page)
        
    4.defaults={"nid":"123456"} 默认参数
    5.strict_slashes=True 是否严格遵循路由地址
    6.redirect_to="/login" 永久重定向 301,在进入当前视图函数之前就重定向

    from flask import Flask,url_for
    app = Flask(__name__)

    @app.route("/index/<page>",methods=["GET","POST"],endpoint="index",defaults={"nid":"1"},strict_slashes=True,redirect_to="/login")
    def index(page, nid):
            # print(page,type(page))
            # print(nid) # 1
            # print(url_for(index)) # /index/<page>
            # print(url_for(login)) # /login
            return "123"

    @app.route("/login",methods=["GET","POST"])
    def login():
            return "login"

    if __name__ == '__main__':
            app.run("0.0.0.0",5000,debug=True)
    




flask实例化配置
    1.template_folder="temp" 默认模板路径 templates
    2.static_folder="static", 默认静态文件路径 static
        3.static_url_path="/static" 访问静态文件路由地址 默认是"/"+static_folder
    
    4.static_host=None 指定静态文件服务器地址
    5.host_matching = False,  # 如果不是特别需要的话,慎用,否则所有的route 都需要host=""的参数
    6.subdomain_matching = False,  # 理论上来说是用来限制SERVER_NAME子域名的,但是目前还没有感觉出来区别在哪里
    7.instance_path = None,  # 指向另一个Flask实例的路径
    8.instance_relative_config = False  # 是否加载另一个实例的配置
    9.root_path = None  # 主模块所在的目录的绝对路径,默认项目目录





flask对象配置 
    'DEBUG': False,  # 是否开启Debug模式
    'TESTING': False,  # 是否开启测试模式
    'SECRET_KEY': None # 在启用Flask内置Session的时候/开启flash,一定要有它
    'PERMANENT_SESSION_LIFETIME': 31,  # days , Session的生命周期(天)默认31天
    'SESSION_COOKIE_NAME': 'session',  # 在cookies中存放session加密字符串的名字

    class FlaskDebug(object):
            DEBUG=True
            SECRET_KEY="DEBUGmoshidesecret_key"
            PERMANENT_SESSION_LIFETIME = 7
            SESSION_COOKIE_NAME = "debug_session"

    app.config.from_object(FlaskDebug)





flask蓝图 (蓝图配置)
    Blueprint 当成一个不能被启动的 app Flask示例
    url_prefix="/blue" url前缀,也可以在注册蓝图时设置该前缀    127.0.0.1:5000/blue/app01

    from flask import Blueprint,render_template
    app01_bp_obj = Blueprint("app01_bp_obj",__name__,template_folder="apptemp",url_prefix="/blue")
    @app01_bp_obj.route("/app01")
    def app01_func():
            return render_template("app01.html")
    
    from app01 import views
    app.register_blueprint(views.app01_bp_obj)   # 在flask实例化对象视图中注册蓝图

    


flask中的flash
    from flask import flash,get_flashed_messages

    flash("message","tag")
    get_flashed_messages()




flask特殊装饰器
    @app.before_request # 请求进入视图函数之前
    @app.after_request # 响应返回客户端之前
    正常情况下流程:be1 - be2 - be3 - af3 - af2 - af1
    异常情况下流程:be1 - af3 - af2 - af1
 
    @app.errorhandler(404) # 重定义错误页面返回信息
    def error404(error_info):
        return 三剑客+小儿子




Flask 的 CBV

from flask import views

class Login(views.MethodView):
    methods = ["GET","POST"]
    decorators = [app.route]

    def get(self):
        print(url_for("my_login"))
        return render_template("login.html")

    def post(self):
        return "登陆成功"

app.add_url_rule("/",view_func=Login.as_view("my_login"))






偏函数

from functools import partial

def ab(a,*args):
    print(a,args)
    return a

par_ab = partial(ab,1)
print(par_ab(2))
print(par_ab)






线程安全

import time
import threading
from threading import local

class Foo(local):
    pass

foo = Foo()

def add(i):
    foo.num=i
    time.sleep(1)
    print(foo.num,i,threading.current_thread().ident)

for i in range(20):
    th = threading.Thread(target=add,args=(i,))
    th.start()
View Code

 

 

一. Python 现阶段三大主流Web框架 Django Tornado Flask 对比

1. Django 主要特点是大而全,集成了很多组件,例如: Models Admin Form 等等, 不管你用得到用不到,反正它全都有,属于全能型框架

    Django 通常用于大型Web应用由于内置组件足够强大所以使用Django开发可以一气呵成

    Django 优点是大而全,缺点也就暴露出来了,这么多的资源一次性全部加载,肯定会造成一部分的资源浪费

2. Tornado 主要特点是原生异步非阻塞,在IO密集型应用和多任务处理上占据绝对性的优势,属于专注型框架

    Tornado 通常用于API后端应用,游戏服务后台,其内部实现的异步非阻塞真是稳得一批

 Tornado 优点是异步,缺点是干净,连个Session都不支持

3. Flask 主要特点小而轻,原生组件几乎为0, 三方提供的组件请参考Django 非常全面,属于短小精悍型框架

    Flask 通常应用于小型应用和快速构建应用,其强大的三方库,足以支撑一个大型的Web应用

    Flask 优点是精悍简单,缺点是稳定性差,若flask更新了而三方库未更新则项目也无法更新!

 

二. Flask 的安装与基本使用

pip install Flask

Flask安装完成了,下面使用Flask走一遍仪式:

实现了Flask 的第一个HelloWorld程序,

解读一下代码

启动了Flask,得到了返回值,打印在页面上

 

三、Flask 中的 Render Redirect HttpResponse

1.Flask中的HTTPResponse其实就是直接返回字符串

在Flask 中的HttpResponse 在我们看来其实就是直接返回字符串

2.Flask中的redirect

每当访问"/redi"这个地址的时候,视图函数redi会触发redirect("/") 跳转到url地址:  "/" 并会触发"/"对应的视图函数index()

3.Flask 中的 render 是 render_template,返回模板由浏览器渲染

HTML模板渲染是每个Web框架中都必须有的

注意: 如果要使用 render_template 返回渲染的模板,请在项目的主目录中加入一个目录 templates

否则可能会有一个Jinja2的异常

遇到上述的问题,基本上就是你的templates的路径问题解决方法如下;

 补充:

Flask中的小儿子jsonify与send_file:
1. from flask import jsonify
   return jsonify({name:111}) # 返回json标准的字符串 
Content-Type:application/json

2. from flask import send_file
   return send_file(path)
# 打开文件并返回文件内容(自动识别文件格式)
Flask中的 jsonify 与 send_file

 

四、Flask 中的 request 

from flask import request
request.method # 请求方式
request.form # 存放FormData中的数据 to_dict 序列化成字典
request.args # 获取URL中的数据 to_dict 序列化成字典
request.url # 访问的完整路径
request.path # 路由地址
request.host # 主机地址
request.values # 获取 FormData and URL中的数据 不要用to_dict
request.json  # 如果提交时请求头中的Content-Type:application/json 字典操作
request.data  # 如果提交时请求头中的Content-Type 无法被识别 将请求体中的原始数据存放 byte
request.cookies # 获取Cookie中的数据
request.headers # 获取请求头
request.files # 序列化文件存储 save()
View Code

每个框架中都有处理请求的机制(request),但是每个框架的处理方式和机制是不同的

为了了解Flask的request中都有什么东西,首先我们要写一个前后端的交互

基于HTML + Flask 写一段前后端的交互

先写一段儿HTML form表单中提交方式是post  action地址是 /req

写好一个标准 form 表单,一点提交,就向后端提交一个POST请求过去了

后端的接收如下:

首先要从 flask 包中导入 request 模块 , request 如果要用,就需要导入

 

解释一个 @app.route("/req",methods=["POST"]) :

methods=["POST"]  代表这个url地址只允许 POST 请求,是个列表也就是意味着可以允许多重请求方式,例如GET之类的

1.request.method

  Flask 的 request 中给我们提供了一个 method 属性里面保存的就是前端的请求的方式

print(request.method) # POST 看来可以使用这种方式来验证请求方式了

2.request.form 

  Form表单中传递过来的值 使用 request.form 中拿到

    print(request.form)  # ImmutableMultiDict([('user', 'Oldboy'), ('pwd', 'DragonFire')])
    # ImmutableMultiDict 它看起来像是Dict 就用Dict的方法取值试一下
    print(request.form["user"])  # Oldboy
    print(request.form.get("pwd"))  # DragonFire
    # 看来全部才对了, ImmutableMultiDict 似乎就是个字典,再来玩一玩它
    print(list(request.form.keys()))  # ['user', 'pwd'] 看来是又对了
    #如果以上所有的方法你都觉得用的不爽的话
    req_dict = dict(request.form)
    print(req_dict)  # 如果你觉得用字典更爽的话,也可以转成字典操作(这里有坑)

3.request.args 之 你能看见的Url参数全在里面

  request.args 中保存的是url中传递的参数

  先把后端请求代码改动一下:

然后使用URL地址直接传递参数

然后会在控制台中看到 ImmutableMultiDict([('id', '1'), ('age', '20')])

哎呀我去,这不是和刚才一样吗? 是的!

    print(request.args)  # ImmutableMultiDict([('id', '1'), ('age', '20')])
    print(request.args["id"])  # 1
    print(request.args.get("age"))  # 20
    print(list(request.args.keys()))  # ['id', 'age']
    print(list(request.args.values()))  # ['1', '20']
    req_dict = dict(request.args)  # {'id': ['1'], 'age': ['20']}
    print(req_dict)

request.args 与 request.form 的区别就是:

  request.args 是获取url中的参数

  request.form 是获取form表单中的参数

4.request.values 之 只要是个参数我都要

改动一下前端代码:

这是让我们在使用form表单提交的同时使用url参数提交

print(request.values)  # CombinedMultiDict([ImmutableMultiDict([('id', '1'), ('age', '20')]), ImmutableMultiDict([('user', 'Oldboy'), ('pwd', 'DragonFire')])])
print(request.values.get("id"))  # 1
print(request.values["user"])  # Oldboy
# 这回喜欢直接操作字典的小伙伴们有惊喜了! to_dict() 方法可以直接将我们的参数全部转为字典形式
print(request.values.to_dict()) # {'user': 'Oldboy', 'pwd': 'DragonFire', 'id': '1', 'age': '20'}

注意啦!注意啦!

# 注意这里的坑来啦! 坑来啦!
# 如果url和form中的Key重名的话,form中的同名的key中value会被url中的value覆盖
# http://127.0.0.1:5000/req?id=1&user=20
print(request.values.to_dict())  # {'user': 20 'pwd': 'DragonFire', 'id': '1'}

5.request.cookies 之 存在浏览器端的字符串儿也会一起带过来

  前提是你要开启浏览器的 cookies

  request.cookies 是将cookies中信息读取出来

6.request.headres 之 请求头中的秘密

用来获取本次请求的请求头

    print(type(request.headers))
    """
    Host: 127.0.0.1:5000
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
    Accept-Encoding: gzip, deflate
    Referer: http://127.0.0.1:5000/home
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 26
    Cookie: csrftoken=vDIozqveCEfArdYXlM6goHVlSQEn7h4bDygNphL2Feas60DiM2di0jlqKfxo7xhA
    Connection: keep-alive
    Upgrade-Insecure-Requests: 1
    Cache-Control: max-age=0
    """

7.request.data 之 如果处理不了的就变成字符串儿存在data里面

  你一定要知道 request 是基于 mimetype 进行处理的

  mimetype的类型 以及 字符串儿 : http://www.w3school.com.cn/media/media_mimeref.asp

  如果不属于上述类型的描述,request就会将无法处理的参数转为Json存入到 data 中

  其实我们可以将 request.data , json.loads 同样可以拿到里面的参数

8.request.files 之 给我一个文件我帮你保管

  如果遇到文件上传的话,request.files 里面存的是你上传的文件,但是 Flask 在这个文件的操作中加了一定的封装,让操作变得极为简单

首先改下前端代码:

后端这样写

    print(request.files)  # ImmutableMultiDict([('file', <FileStorage: 'DragonFire.txt' ('text/plain')>)])
    print(request.files["file"])  # <FileStorage: 'DragonFire.txt' ('text/plain')>
    my_file = request.files["file"]
    my_file.save("OldBoyEDU.txt")  # 保存文件,里面可以写完整路径+文件名

这样我们就成功的保存了一个名叫 "OldBoyEDU.txt" 的文件了,操作还是很简单的

9. request.获取各种路径 之 这些方法没必要记,但是要知道它存在

    # 获取当前的url路径
    print(request.path)# /req
    # 当前url路径的上一级路径
    print(request.script_root) #
    # 当前url的全部路径
    print(request.url) # http://127.0.0.1:5000/req
    # 当前url的路径的上一级全部路径
    print(request.url_root ) # http://127.0.0.1:5000/

10. request.json 之 前提你得告诉是json

如果在请求中写入了 "application/json" 使用 request.json 则返回json解析数据, 否则返回 None

 

 

五、Flask 中的模板语言 Jinja2 及 render_template 的深度用法

Flask中默认的模板语言是Jinja2

首先我们要在后端定义几个字符串,用于传递到前端

STUDENT = {'name': 'Old', 'age': 38, 'gender': '中'},

STUDENT_LIST = [
    {'name': 'Old', 'age': 38, 'gender': '中'},
    {'name': 'Boy', 'age': 73, 'gender': '男'},
    {'name': 'EDU', 'age': 84, 'gender': '女'}
]

STUDENT_DICT = {
    1: {'name': 'Old', 'age': 38, 'gender': '中'},
    2: {'name': 'Boy', 'age': 73, 'gender': '男'},
    3: {'name': 'EDU', 'age': 84, 'gender': '女'},
}

但是前提我们要知道Jinja2模板中的流程控制:

I. Jinja2模板语言中的 for

{% for foo in g %}

{% endfor %}

II. Jinja2模板语言中的 if

{% if g %}

{% elif g %}
    
{% else %}
    
{% endif %}

接下来,我们对这几种情况分别进行传递,并在前端显示成表格

1. 使用STUDENT字典传递至前端

后端:

@app.route("/student")
def index():
    return render_template("student.html", student=STUDENT)

前端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Old Boy EDU</title>
</head>
<body>
Welcome to Old Boy EDU
<div>{{ student }}</div>
<table border="1px">
    <tr>
        <td>{{ student.name }}</td>
        <td>{{ student["age"] }}</td>
        <td>{{ student.get("gender") }}</td>
    </tr>
</table>
</body>
</html>

结果:

从这个例子中,可以看出来,字典传入前端Jinja2 模板语言中的取值操作, 与Python中的Dict操作极为相似,并且多了一个student.name的对象操作

2. STUDENT_LIST 列表传入前端Jinja2 模板的操作:

后端:

@app.route("/student_list")
def student_list():
    return render_template("student_list.html", student=STUDENT_LIST)

前端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Old Boy EDU</title>
</head>
<body>
Welcome to Old Boy EDU
<div>{{ student }}</div>
<table border="1xp">
    {% for foo in student %}
        <tr>
            <td>{{ foo }}</td>
            <td>{{ foo.name }}</td>
            <td>{{ foo.get("age") }}</td>
            <td>{{ foo["gender"] }}</td>
        </tr>
    {% endfor %}
</table>
</body>
</html>

结果:

这里我们可以看出如果是需要循环遍历的话,Jinja2 给我们的方案是

    {% for foo in student %}
        <tr>
            <td>{{ foo }}</td>
        </tr>
    {% endfor %}

上述代码中的 foo 就是列表中的每一个字典,再使用各种取值方式取出值即可

3.STUDENT_DICT 大字典传入前端 Jinja2 模板

后端:

@app.route("/student_dict")
def student_dict():
    return render_template("student_dict.html", student=STUDENT_DICT)

前端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Old Boy EDU</title>
</head>
<body>
Welcome to Old Boy EDU
<table>
    {% for foo in student %}
        <tr>
            <td>{{ foo }}</td>
            <td>{{ student.get(foo).name }}</td>
            <td>{{ student[foo].get("age") }}</td>
            <td>{{ student[foo]["gender"] }}</td>
        </tr>
    {% endfor %}
</table>
</body>
</html>

在遍历字典的时候,foo 其实是相当于拿出了字典中的Key

结果:

4.结合所有的字符串儿全部专递前端Jinja2 模板

后端:

@app.route("/allstudent")
def all_student():
    return render_template("all_student.html", student=STUDENT ,
                           student_list = STUDENT_LIST,
                           student_dict= STUDENT_DICT)

前端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Old Boy EDU</title>
</head>
<body>
<div> _____________________________________</div>
Welcome to Old Boy EDU : student
<div>{{ student }}</div>
<table border="1px">
    <tr>
        <td>{{ student.name }}</td>
        <td>{{ student["age"] }}</td>
        <td>{{ student.get("gender") }}</td>
    </tr>
</table>
<div> _____________________________________</div>
Welcome to Old Boy EDU : student_list
<div>{{ student_list }}</div>
<table border="1xp">
    {% for foo in student_list %}
        <tr>
            <td>{{ foo }}</td>
            <td>{{ foo.name }}</td>
            <td>{{ foo.get("age") }}</td>
            <td>{{ foo["gender"] }}</td>
        </tr>
    {% endfor %}
</table>
<div> _____________________________________</div>
Welcome to Old Boy EDU : student_dict
<div>{{ student_dict }}</div>
<table border="1xp">
    {% for foo in student_dict %}
        <tr>
            <td>{{ foo }}</td>
            <td>{{ student_dict.get(foo).name }}</td>
            <td>{{ student_dict[foo].get("age") }}</td>
            <td>{{ student_dict[foo]["gender"] }}</td>
        </tr>
    {% endfor %}
</table>
</body>
</html>

结果:

这里可以看出来,render_template中可以传递多个关键字

5.利用 **{}字典的方式传递参数

前端不变(标题4的前端代码)

后端:

@app.route("/allstudent")
def all_student():
    return render_template("all_student.html", **{"student":STUDENT ,
                           "student_list" : STUDENT_LIST,
                           "student_dict": STUDENT_DICT})

6. Jinja2 的高阶用法

Jinja2 模板语言为我们提供了很多功能接下来看一下它有什么高级的用法

6.1. safe : 此时你与HTML只差一个 safe

后端代码:

from flask import Flask
from flask import render_template

app = Flask(__name__)

@app.route("/")
def index():
    tag = "<input type='text' name='user' value='DragonFire'>"
    return render_template("index.html",tag=tag)


app.run("0.0.0.0",5000)

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {{ tag }}
</body>
</html>
前端代码

如果我们直接运行代码直接访问,你会在页面看到什么呢?

似乎和我们想要结果不太一样,有两种解决方案,

第一种,从前端入手

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {{ tag | safe}}  <!--  加上个 \  管道符,然后 safe  -->
</body>
</html>
前端代码

还有一种方式是从后端入手

后端代码:

from flask import Flask
from flask import render_template
from flask import Markup  # 导入 flask 中的 Markup 模块

app = Flask(__name__)


@app.route("/")
def index():
    tag = "<input type='text' name='user' value='DragonFire'>"
    markup_tag = Markup(tag)  # Markup帮助咱们在HTML的标签上做了一层封装,让Jinja2模板语言知道这是一个安全的HTML标签
    print(markup_tag,
          type(markup_tag))  # <input type='text' name='user' value='DragonFire'> <class 'markupsafe.Markup'>
    return render_template("index.html", tag=markup_tag)


app.run("0.0.0.0", 5000, debug=True)

两种得到的效果是一样

6.2 在Jinja2中执行Python函数(模板中执行函数)

首先在文件中定义一个函数

后端代码:

from flask import Flask
from flask import render_template
from flask import Markup  # 导入 flask 中的 Markup 模块

app = Flask(__name__)

#定义一个函数,把它传递给前端
def a_b_sum(a,b):
    return a+b

@app.route("/")
def index():
    return render_template("index.html", tag=a_b_sum)


app.run("0.0.0.0", 5000, debug=True)

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {{ tag }}
    <br>
    {{ tag(99,1) }}
</body>
</html>

看到结果就是,函数加()执行得到结果

还可以定义全局函数,无需后端传递给前端,Jinja2直接就可以执行的函数

后端代码:

from flask import Flask
from flask import render_template
from flask import Markup  # 导入 flask 中的 Markup 模块

app = Flask(__name__)


@app.template_global()  # 定义全局模板函数
def a_b_sum(a, b):
    return a + b


@app.template_filter()  # 定义全局模板函数
def a_b_c_sum(a, b, c):
    return a + b + c


@app.route("/")
def index():
    return render_template("index.html", tag="")


app.run("0.0.0.0", 5000, debug=True)

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {{ a_b_sum(99,1) }}
    <br>
    {{ 1 | a_b_c_sum(197,2) }}
</body>
</html>

两个函数的调用方式不太一样

尤其是@app.template_filter() 它的调用方式比较特别,这是两个Flask中的特殊装饰器

6.3 Jinja2模板复用 block

如果我们前端页面有大量重复页面,没必要每次都写,可以使用模板复用的方式复用模板

前端代码:

index.html 文件中的内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>Welcome OldboyEDU</h1>
    <h2>下面的内容是不一样的</h2>
    {% block content %}

    {% endblock %}
    <h2>上面的内容是不一样的,但是下面的内容是一样的</h2>
    <h1>OldboyEDU is Good</h1>
</body>
</html>
index.html

login.html 文件中的内容

{% extends "index.html" %}
{% block content %}
    <form>
        用户名:<input type="text" name="user">
        密码:<input type="text" name="pwd">
    </form>
{% endblock %}
login.html

home.html 文件中的内容

{% extends "index.html" %}
{% block content %}
    <h1>欢迎来到老男孩教育</h1>
{% endblock %}
home.html

后端代码:

from flask import Flask
from flask import render_template

app = Flask(__name__)


@app.route("/login")
def login():
    return render_template("login.html")


@app.route("/home")
def home():
    return render_template("home.html")


app.run("0.0.0.0", 5000, debug=True)

然后我们可以看到什么呢?

大概是这样一个效果

在这两个页面中,只有 block 中的内容发生了变化,其他的位置完全一样

6.4 Jinja2模板语言的模块引用 include

login.html 文件中的内容:

<form>
    用户名:<input type="text" name="user">
    密码:<input type="text" name="pwd">
</form>

index.html 文件中的内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>Welcome OldboyEDU</h1>
    <h2>下面的内容是不一样的</h2>
    {% include "login.html" %}
    <h2>上面的内容是不一样的,但是下面的内容是一样的</h2>
    <h1>OldboyEDU is Good</h1>
</body>
</html>

后端代码:

from flask import Flask
from flask import render_template

app = Flask(__name__)


@app.route("/")
def index():
    return render_template("index.html")


app.run("0.0.0.0", 5000, debug=True)

看到的结果

这就是将 login.html 当成一个模块,加载到 index.html 页面中

6.5 Jinja2模板语言中的宏定义

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1>Welcome OldboyEDU</h1>

{% macro type_text(name,type) %}
    <input type="{{ type }}" name="{{ name }}" value="{{ name }}">
{% endmacro %}

<h2>在下方是使用宏来生成input标签</h2>

{{ type_text("one","text") }}
{{ type_text("two","text") }}

</body>
</html>

宏定义一般情况下很少应用到,但是要知道有这么个概念

做一个用户登录之后查看学员信息的小例子

需求:

1. 用户名: oldboy 密码: oldboy123

2. 用户登录成功之后跳转到列表页面

3. 失败有消息提示,重新登录

4.点击学生名称之后,可以看到学生的详细信息

后端:

from flask import Flask
from flask import request
from flask import render_template
from flask import redirect

USER = {'username': 'oldboy', 'password': "oldboy123"}

STUDENT_DICT = {
    1: {'name': 'Old', 'age': 38, 'gender': ''},
    2: {'name': 'Boy', 'age': 73, 'gender': ''},
    3: {'name': 'EDU', 'age': 84, 'gender': ''},
}

app = Flask(__name__)


@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        if request.form["username"] == USER["username"] and request.form["password"] == USER["password"]:
            return redirect("/student_list")
        return render_template("login.html", msg="用户名密码错误")

    return render_template("login.html", msg=None)  # 如果前端Jinja2模板中使用了msg,这里就算是传递None也要出现msg


@app.route("/student_list")
def student():
    return render_template("student_list.html", student=STUDENT_DICT)


@app.route("/info")
def student_info():
    stu_id = int(request.args["id"])
    stu_info = STUDENT_DICT[stu_id]
    return render_template("student.html", student=stu_info, stu_id=stu_id)


app.run("0.0.0.0", 5000, debug=True)
相信写的代码才是最好的

前端:

login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Welcome to Old Boy EDU</title>
</head>
<body>
    <form method="post">
        用户名:<input type="text" name="username">
        密码:<input type="text" name="password">
        <input type="submit" value="登录">
        {{ msg }}
    </form>
</body>
</html>
login.html

student_list.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Old Boy EDU</title>
</head>
<body>
Welcome to Old Boy EDU
<table border="2xp">
    <thead>
        <tr>
            <td>id</td>
            <td>name</td>
            <td>option</td>
        </tr>
    </thead>
    <tbody>
        {% for foo in student %}
            <tr>
                <td>{{ foo }}</td>
                <td>{{ student[foo].name }}</td>
                <td><a href="/info?id={{ foo }}">详细</a></td>
            </tr>
        {% endfor %}
    </tbody>
</table>
</body>
</html>
student_list.html

student.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Old Boy EDU</title>
</head>
<body>
Welcome to Old Boy EDU
<table border="1px">
    <thead>
    <tr>
        <td>id</td>
        <td>name</td>
        <td>age</td>
        <td>gender</td>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>{{ stu_id }}</td>
        <td>{{ student.name }}</td>
        <td>{{ student["age"] }}</td>
        <td>{{ student.get("gender") }}</td>
    </tr>
    </tbody>
</table>
<div><a href="/student_list">返回</a></div>
</body>
</html>
student.html

问题:

  1.如果我直接访问 /student_list 和 /student 是不是也可以?

  2.怎么才能在所有的url地址中校验是否登录?

 

 

 

六、Flask 中内置的 Session

Flask中的Session非常的奇怪,他会将你的SessionID存放在客户端的Cookie中,使用起来也非常的奇怪

1. Flask 中 session 是需要 secret_key 的

from flask import session
app = Flask(__name__)
app.secret_key = "DragonFire"

secret_key 实际上是用来加密字符串的,如果在实例化的app中没有 secret_key 那么开启session一定会抛异常的

2. session 要这样用

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        if request.form["username"] == USER["username"] and request.form["password"] == USER["password"]:
            session["user"] = USER["username"]
            return redirect("/student_list")
        return render_template("login.html", msg="用户名密码错误")

    return render_template("login.html", msg=None)  # 如果前端Jinja2模板中使用了msg,这里就算是传递None也要出现msg

session["user"] = USER["username"] 这样用就代表这个请求带上来的session中保存了一个user=name
如果想要验证session的话,就用这种方法吧

3. cookies 中的 session 是什么

cookies 中 session 存储的是通过 secret_key 加密后的 key , 通过这个 key 从flask程序的内存中找到用户对应的session信息

4. 怎么用 session 进行验证呢?

@app.route("/student_list")
def student():
    if session.get("user"):
        return render_template("student_list.html", student=STUDENT_DICT)

    return redirect("/login")

 

 

 

七、Flask 中路由系统

1. @app.route() 装饰器中的参数

methods : 当前 url 地址,允许访问的请求方式

@app.route("/info", methods=["GET", "POST"])
def student_info():
    stu_id = int(request.args["id"])
    return f"Hello Old boy {stu_id}"  # Python3.6的新特性 f"{变量名}"

endpoint : 反向url地址,默认为视图函数名 (url_for)

from flask import url_for


@app.route("/info", methods=["GET", "POST"], endpoint="r_info")
def student_info():
    print(url_for("r_info"))  # /info
    stu_id = int(request.args["id"])
    return f"Hello Old boy {stu_id}"  # Python3.6的新特性 f"{变量名}"

defaults : 视图函数的参数默认值{"nid":1}

from flask import url_for


@app.route("/info", methods=["GET", "POST"], endpoint="r_info", defaults={"nid": 100})
def student_info(nid):
    print(url_for("r_info"))  # /info
    # stu_id = int(request.args["id"])
    print(nid)  # 100
    return f"Hello Old boy {nid}"  # Python3.6的新特性 f"{变量名}"

strict_slashes : url地址结尾符"/"的控制 False : 无论结尾 "/" 是否存在均可以访问 , True : 结尾必须不能是 "/"

# 访问地址 : /info 
@app.route("/info", strict_slashes=True)
def student_info():
    return "Hello Old boy info"


# 访问地址 : /infos  or  /infos/
@app.route("/infos", strict_slashes=False)
def student_infos():
    return "Hello Old boy infos"

redirect_to : url地址重定向

# 访问地址 : /info 浏览器跳转至 /infos
@app.route("/info", strict_slashes=True, redirect_to="/infos")
def student_info():
    return "Hello Old boy info"

@app.route("/infos", strict_slashes=False)
def student_infos():
    return "Hello Old boy infos"

subdomain : 子域名前缀 subdomian="DragonFire" 这样写可以得到 DragonFire.oldboyedu.com 前提是app.config["SERVER_NAME"] = "oldboyedu.com"

app.config["SERVER_NAME"] = "oldboy.com"

@app.route("/info",subdomain="DragonFire")
def student_info():
    return "Hello Old boy info"

# 访问地址为:  DragonFire.oldboy.com/info

2.动态参数路由:

from flask import url_for


# 访问地址 : http://127.0.0.1:5000/info/1
@app.route("/info/<int:nid>", methods=["GET", "POST"], endpoint="r_info")
def student_info(nid):
    print(url_for("r_info",nid=2))  # /info/2
    return f"Hello Old boy {nid}"  # Python3.6的新特性 f"{变量名}"

<int:nid> 就是在url后定义一个参数接收

但是这种动态参数路由,在url_for的时候,一定要将动态参数名+参数值添加进去,否则会抛出参数错误的异常

3.路由正则:

一般不用,如果有特殊需求,不怕麻烦的话,这个东西还是挺好用的

 

 

 

八、实例化Flask的参数 及 对app的配置

Flask 是一个非常灵活且短小精干的web框架 , 那么灵活性从什么地方体现呢?

有一个神奇的东西叫 Flask配置 ,首先展示一下:

from flask import Flask

app = Flask(__name__)  # type:Flask
app.config["DEBUG"] = True

这句 app.config["DEBUG"] = True 可以实现的功能是 代码只要发生改动,自动重启Flask程序(app.run)

在控制台打印的信息非常全面

以上两个功能就是 DEBUG 模式(调试模式)

Flask的配置就是在 app.config 中添加一个键值对,但是你存进去的键必须是config中应该存在的,如果不再存在的话,它会默认无用,就这么放着

config中有多少有用的key 呢?

{
    'DEBUG': False,  # 是否开启Debug模式
    'TESTING': False,  # 是否开启测试模式
    'PROPAGATE_EXCEPTIONS': None,  # 异常传播(是否在控制台打印LOG) 当Debug或者testing开启后,自动为True
    'PRESERVE_CONTEXT_ON_EXCEPTION': None,  # 一两句话说不清楚,一般不用它
    'SECRET_KEY': None,  # 之前遇到过,在启用Session的时候,一定要有它
    'PERMANENT_SESSION_LIFETIME': 31,  # days , Session的生命周期(天)默认31天
    'USE_X_SENDFILE': False,  # 是否弃用 x_sendfile
    'LOGGER_NAME': None,  # 日志记录器的名称
    'LOGGER_HANDLER_POLICY': 'always',
    'SERVER_NAME': None,  # 服务访问域名
    'APPLICATION_ROOT': None,  # 项目的完整路径
    'SESSION_COOKIE_NAME': 'session',  # 在cookies中存放session加密字符串的名字
    'SESSION_COOKIE_DOMAIN': None,  # 在哪个域名下会产生session记录在cookies中
    'SESSION_COOKIE_PATH': None,  # cookies的路径
    'SESSION_COOKIE_HTTPONLY': True,  # 控制 cookie 是否应被设置 httponly 的标志,
    'SESSION_COOKIE_SECURE': False,  # 控制 cookie 是否应被设置安全标志
    'SESSION_REFRESH_EACH_REQUEST': True,  # 这个标志控制永久会话如何刷新
    'MAX_CONTENT_LENGTH': None,  # 如果设置为字节数, Flask 会拒绝内容长度大于此值的请求进入,并返回一个 413 状态码
    'SEND_FILE_MAX_AGE_DEFAULT': 12,  # hours 默认缓存控制的最大期限
    'TRAP_BAD_REQUEST_ERRORS': False,
    # 如果这个值被设置为 True ,Flask不会执行 HTTP 异常的错误处理,而是像对待其它异常一样,
    # 通过异常栈让它冒泡地抛出。这对于需要找出 HTTP 异常源头的可怕调试情形是有用的。
    'TRAP_HTTP_EXCEPTIONS': False,
    # Werkzeug 处理请求中的特定数据的内部数据结构会抛出同样也是“错误的请求”异常的特殊的 key errors 。
    # 同样地,为了保持一致,许多操作可以显式地抛出 BadRequest 异常。
    # 因为在调试中,你希望准确地找出异常的原因,这个设置用于在这些情形下调试。
    # 如果这个值被设置为 True ,你只会得到常规的回溯。
    'EXPLAIN_TEMPLATE_LOADING': False,
    'PREFERRED_URL_SCHEME': 'http',  # 生成URL的时候如果没有可用的 URL 模式话将使用这个值
    'JSON_AS_ASCII': True,
    # 默认情况下 Flask 使用 ascii 编码来序列化对象。如果这个值被设置为 False ,
    # Flask不会将其编码为 ASCII,并且按原样输出,返回它的 unicode 字符串。
    # 比如 jsonfiy 会自动地采用 utf-8 来编码它然后才进行传输。
    'JSON_SORT_KEYS': True,
    #默认情况下 Flask 按照 JSON 对象的键的顺序来序来序列化它。
    # 这样做是为了确保键的顺序不会受到字典的哈希种子的影响,从而返回的值每次都是一致的,不会造成无用的额外 HTTP 缓存。
    # 你可以通过修改这个配置的值来覆盖默认的操作。但这是不被推荐的做法因为这个默认的行为可能会给你在性能的代价上带来改善。
    'JSONIFY_PRETTYPRINT_REGULAR': True,
    'JSONIFY_MIMETYPE': 'application/json',
    'TEMPLATES_AUTO_RELOAD': None,
}

以上这些Key,都可以被改写,当然他们也都是有默认值存在的,如果没有特殊情况,不要改写它的默认值

修改配置的方式大约是两种

1.直接对app.config进行修改

app.config["DEBUG"] = True

2.使用类的方式导入

首先要有一个settings.py的文件

class FlaskSetting:
    DEBUG = True
    SECRET_KEY = "DragonFire"

然后我们在Flask的启动文件中就可以这么写

from flask import Flask


app = Flask(__name__)  # type:Flask
app.config.from_object("settings.FlaskSetting")

这叫做类导入配置

这是针对一个已经实例化的app进行的配置

那么在Flask实例化的时候,传递的参数是什么鬼呢?

其实可以理解为对Flask实例进行的初始配置,这里面的参数非常好理解,注意关键字非常好理解

static_folder = 'static',  # 静态文件目录的路径 默认当前项目中的static目录
static_host = None,  # 远程静态文件所用的Host地址,默认为空
static_url_path = None,  # 静态文件目录的url路径 默认不写是与static_folder同名,远程静态文件时复用
# host_matching是否开启host主机位匹配,是要与static_host一起使用,如果配置了static_host, 则必须赋值为True
# 这里要说明一下,@app.route("/",host="localhost:5000") 就必须要这样写
# host="localhost:5000" 如果主机头不是 localhost:5000 则无法通过当前的路由
host_matching = False,  # 如果不是特别需要的话,慎用,否则所有的route 都需要host=""的参数
subdomain_matching = False,  # 理论上来说是用来限制SERVER_NAME子域名的,但是目前还没有感觉出来区别在哪里
template_folder = 'templates'  # template模板目录, 默认当前项目中的 templates 目录
instance_path = None,  # 指向另一个Flask实例的路径
instance_relative_config = False  # 是否加载另一个实例的配置
root_path = None  # 主模块所在的目录的绝对路径,默认项目目录

这里面,我们常用的参数有

static_folder = 'static',  # 静态文件目录的路径 默认当前项目中的static目录
static_url_path = None,  # 静态文件目录的url路径 默认不写是与static_folder同名,远程静态文件时复用
template_folder = 'templates'  # template模板目录, 默认当前项目中的 templates 目录

记住这些就好了,一般的项目中,只修改这些参数

 

 

 

九、Flask 中的蓝图(BluePrint)

Flask中的蓝图 blueprint  的作用就是将 功能 与 主服务 分开

        比如说,你有一个客户管理系统,最开始的时候,只有一个查看客户列表的功能,后来你又加入了一个添加客户的功能(add_user)模块, 然后又加入了一个删除客户的功能(del_user)模块,然后又加入了一个修改客户的功能(up_user)模块,在这个系统中,就可以将 查看客户,修改客户,添加客户,删除客户的四个功能做成蓝图加入到客户管理系统中

1.初识Flask蓝图(blueprint)

创建一个项目然后将目录结构做成:

s_view.py 文件中的内容

from flask import Blueprint  # 导入 Flask 中的蓝图 Blueprint 模块

sv = Blueprint("sv", __name__)  # 实例化一个蓝图(Blueprint)对象


@sv.route("/svlist")  # 这里添加路由和视图函数的时候与在Flask对象中添加是一样的
def view_list():
    return "svlist_view_list"

manager.py 文件中的内容

from flask import Flask

# 导入此前写好的蓝图模块
from student_view import s_view

app = Flask(__name__)  # type:Flask

# 在Flask对象中注册蓝图模块中的蓝图对象 s_view 中的 sv
app.register_blueprint(s_view.sv)

app.run("0.0.0.0",5000)
# 现在Flask对象中并没有写任何的路由和视图函数

开启服务,然后访问 http://127.0.0.1:5000/svlist 查看结果

很明显,我们没有在Flask对象中添加路由,但是我们注册了有路由和视图函数的sv蓝图对象

2.如何理解蓝图呢?

其实我们可以理解成一个没有run方法的Flask对象

下面来看一下,在实例化蓝图的时候可以传递的参数都有什么

这是目录结构

s_view.py 文件中的内容 :

from flask import Blueprint  # 导入 Flask 中的蓝图 Blueprint 模块
from flask import render_template

sv = Blueprint("sv",
               __name__,
               template_folder="sv_template",  # 每个蓝图都可以为自己独立出一套template模板文件夹,如果不写则共享项目目录中的templates
               static_folder="sv_static"  # 静态文件目录也是可以独立出来的
               )  # 实例化一个蓝图(Blueprint)对象


@sv.route("/svlist")
def view_list():
    return render_template("svlist.html")

svlist.html 文件中的内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    Hello ! I am sv_template
    <img src="/sv_static/DragonFire.png">
</body>
</html>

打开页面看结果

从这个例子中我们总结出:

  Blueprint 其实可以理解为一个了没有run方法的 Flask 对象

  只要Blueprint被 Flask 注册了,就一定会生效

  坑来了! 蓝图内部的视图函数及route不要出现重复,否则~你们自己试试吧

3.使用蓝图,做一个增删改查用户

要有一个文件存放我们的原始数据

student_data.py 文件中的内容:

STUDENT = [
    {'id': 1, 'name': 'Old', 'age': 38, 'gender': '中'},
    {'id': 2, 'name': 'Boy', 'age': 73, 'gender': '男'},
    {'id': 3, 'name': 'EDU', 'age': 84, 'gender': '女'}
]

然后我们根据以上内容进行增删改查

3.1 使用蓝图进行web应用搭建:

__init__.py 文件中的内容:

from flask import Flask


def create_app():
    app = Flask(__name__)

    return app

这个文件我们会修改函数 create_app中的代码

manager.py 文件中的内容

from student import create_app

flask_app = create_app()

flask_app.run("0.0.0.0",5000)

通过这种方式启动 Flask 程序

3.2 使用Flask蓝图,查看学生信息

s_list.html 文件中的内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学生列表</title>
</head>
<body>
<table border="3xp">
    <thead>
    <tr>
        <td>ID</td>
        <td>name</td>
        <td>age</td>
        <td>gender</td>
        <td>options</td>
    </tr>
    </thead>
    <tbody>
    {% for foo in student %}
        <tr>
            <td>{{ foo.id }}</td>
            <td>{{ foo["name"] }}</td>
            <td>{{ foo.get("age") }}</td>
            <td>{{ foo.gender }}</td>
            <td> <a href="/s_update/{{ foo.id }}">修改</a> | <a href="/s_del?id={{ foo.id }}">删除</a> </td>
        </tr>
    {% endfor %}
    </tbody>
</table>
<a href="/s_add"> 添加学生 </a>
</body>
</html>
s_list.html

stu_select.py 文件中的内容:

from flask import Blueprint
from flask import render_template
from student_data import STUDENT

ss_blueprint = Blueprint("ss_b", __name__, template_folder="html", static_folder="static")


@ss_blueprint.route("/s_list")
def s_list():
    return render_template("s_list.html", student=STUDENT)
stu_select.py

student/__init__.py 文件中的内容:

from flask import Flask
from student_select import stu_select


def create_app():
    app = Flask(__name__)  # type:Flask

    app.register_blueprint(stu_select.ss_blueprint)

    return app
student/__init__.py

运行一下manager.py 来访问一下

什么链接都不要点,因为点啥都不好使,之后会一个一个的做

3.3. 使用Flask蓝图,添加一个学生

s_add.html 文件中的内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学生列表</title>
</head>
<body>
<form method="post">
    ID:<input type="text" name="id"> <br>
    姓名:<input type="text" name="name"><br>
    年龄:<input type="text" name="age"><br>
    性别:<input type="text" name="gender"><br>
    <input type="submit" value="添加学生">
</form>

</body>
</html>
s_add.html

stu_add.py 文件中的内容

from flask import Blueprint
from flask import redirect
from flask import request
from flask import render_template
from student_data import STUDENT

s_add = Blueprint("s_add", __name__, template_folder="html", static_folder="static") # type:Blueprint


@s_add.route("/s_add",methods=["GET","POST"])
def s_add_view():
    if request.method == "POST":
        stu_dic = {
            "id": request.form["id"],
            "name": request.form["name"],
            "age": request.form["age"],
            "gender": request.form["gender"]
        }

        STUDENT.append(stu_dic)

        return redirect("/s_list")

    return render_template("s_add.html")
stu_add.py

这里面我们让他添加完一个学生,就返回到s_list查看学生列表

student/__init__.py 文件中的内容

from flask import Flask
from student_select import stu_select
from student_add import stu_add


def create_app():
    app = Flask(__name__)  # type:Flask

    app.register_blueprint(stu_select.ss_blueprint)
    app.register_blueprint(stu_add.s_add)

    return app
student/__init__.py

如果你要是重新启动服务了,那么你刚刚添加的学生信息就没有了

添加完成之后

添加学生的Blueprint已经做完了

3.4. 使用Flask蓝图,修改学生信息

s_update.html 文件中的内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学生列表</title>
</head>
<body>
<form method="post">
    <input type="text" name="id" hidden value="{{ student.id }}"><br>
    姓名:<input type="text" name="name" value="{{ student.name }}"><br>
    年龄:<input type="text" name="age" value="{{ student.age }}"><br>
    性别:<input type="text" name="gender" value="{{ student.gender }}"><br>
    <input type="submit" value="修改信息">
</form>

</body>
</html>
s_update.html

stu_update.py 文件中的内容:

from flask import Blueprint
from flask import render_template
from flask import redirect
from flask import request
from student_data import STUDENT

s_update = Blueprint("s_update", __name__, template_folder="html", static_folder="static")


@s_update.route("/s_update/<int:nid>",methods=["GET","POST"])
def s_update_view(nid):
    if request.method == "POST":
        stu_id = int(request.form["id"])
        stu_dic = {
            "id": stu_id,
            "name": request.form["name"],
            "age": request.form["age"],
            "gender": request.form["gender"]
        }

        for index,stu in enumerate(STUDENT):
            if stu["id"] == stu_id:
                STUDENT[index] = stu_dic

        return redirect("/s_list")

    for stu in STUDENT:
        if stu["id"] == nid :
            return render_template("s_update.html", student=stu)

    return render_template("s_update.html", student="")
stu_update.py

student/__init__.py 文件中的内容:

from flask import Flask
from student_select import stu_select
from student_add import stu_add
from student_update import stu_update


def create_app():
    app = Flask(__name__)  # type:Flask

    app.register_blueprint(stu_select.ss_blueprint)
    app.register_blueprint(stu_add.s_add)
    app.register_blueprint(stu_update.s_update)

    return app
student/__init__.py

试一下结果:

 修改的功能也已经做完了,自己尝试做一下删除吧

4.蓝图目录:

其实学会了蓝图,我们的Flask项目目录结构也就随之出来了,那么Flask的蓝图目录结构应该是什么样子的呢?

如图,这就是我们建立好的一个目录结构,一层一层的看一下,首先是app目录,它就是我们的主应用程序目录了在这里面有一个__init__.py这个文件里面的内容如下

from flask import Flask
from .views.auto import auto_bp
from .views.motor import motor_bp


def create_app():
    my_app = Flask(__name__) # type:Flask

    my_app.register_blueprint(auto_bp)
    my_app.register_blueprint(motor_bp)

    return my_app
app/__init__.py

由此见得__init__.py就是构建app的一个函数,并且将views中的似乎是蓝图的东西注册进去了

  static目录 就是 static静态文件存放目录

  templates目录,模板存放目录

  views目录 存放的就是视图函数文件,也就是我们Blueprint,每一个文件就是一个Blueprint

from flask import Blueprint

auto_bp = Blueprint("auto",__name__)

@auto_bp.route("/auto")
def auto_func():
    return "my_app.auto"
views/auto.py
from flask import Blueprint

motor_bp = Blueprint("motor",__name__)

@motor_bp.route("/motor")
def motor_func():
    return "my_app.motor"
views/motor.py

这样目录结构就完成了,接下来就是关键性的一个文件manager.py项目的启动文件

from app import create_app
my_app = create_app()

if __name__ == '__main__':
    my_app.run()
manager.py

以上就是Flask小型应用的项目结构目录,要牢记

 

 

 

十、Flask 中的 before_request after_request errorhandler

 现在有一个 Flask 程序其中有3个路由和视图函数,如下:
from flask import Flask

app = Flask(__name__)  # type:Flask


@app.route("/login")
def login():
    return "Login"

@app.route("/index")
def index():
    return "Index"

@app.route("/home")
def home():
    return "Login"

app.run("0.0.0.0", 5000)
简单的小程序

如果登陆了,就可以访问 index 和 home 页面,如果没登录就跳转到 login 登录

要怎么解决呢, session 对, 用 session 除了 Login 函数之外的所有函数里面全校验 session 是否登录了

太麻烦了,现在只有3个函数,如果成百上千个怎么整啊

装饰器是一个很好的方案,但是现在还是要在每一个函数定义的时候加上@装饰器,还是很麻烦

那么就要用@app.before_request

1.@app.before_request 在请求(request)进入视图函数之前执行

from flask import Flask
from flask import request
from flask import redirect
from flask import session

app = Flask(__name__)  # type:Flask
app.secret_key = "DragonFire"


@app.before_request
def is_login():
    if request.path == "/login":
        return None

    if not session.get("user"):
        return redirect("/login")


@app.route("/login")
def login():
    return "Login"


@app.route("/index")
def index():
    return "Index"


@app.route("/home")
def home():
    return "Login"


app.run("0.0.0.0", 5000)
解决所有问题

@app.before_request 也是一个装饰器,他所装饰的函数,都会在请求进入视图函数之前执行

request.path 是来读取当前的url地址如果是 /login 就允许直接通过 return None 你可以理解成通过放行

校验session中是否有user 如果没有的话,证明没有登录,所以毫不留情的 redirect("/login") 跳转登录页面

还有一个要提的 @app.before_first_request 它与 @app.before_request 极为相似或者说是一模一样,只不过它只会被执行一次

2. @app.after_request 在响应(response)返回客户端之前执行 , 结束视图函数之后

@app.after_request
def foot_log(environ):
    if request.path != "/login":
        print("有客人访问了",request.path)
    return environ

很少应用,但是要了解有这么个东西

3.@app.errorhandler(错误码)

  @app.errorhandler(404) # 重定义错误页面返回信息
  def error404(error_info):
    return 三剑客+小儿子

 

 

 

十一、Flask 中的 CBV模式

flask_cbv.py

 1 '''
 2 flask中的CBV模式:
 3     (1)导入views模块: from flask import views
 4     (2)定义类,继承views.MethodView类: class 类名(views.MethodView)
 5     (3)在类中定义函数名为允许的请求方式的小写形式,进行函数定义,如def get(self):...
 6     (4)添加路由规则:
 7         CBV:app.add_url_rule(rule,endpoint='',view_func=类名.as_view(name=''))
 8         FBV:app.add_url_rule(rule,endpoint='',view_func=函数名))(直接替代@app.route()方式)
 9 
10         参数:
11             rule 请求路径
12             endpoint设置mapping路由映射函数rule:{endpoint:func}
13             view_func路由映射的视图函数或者类(as_view()中的name参数设置当前路由映射函数名,唯一指定,不设置endpoint自动为name值)
14     (5)methods=[]类中设置属性,添加允许的请求方式,不写默认都支持
15     (6)decorators = []对类中的函数都加上装饰器,列表中可以放多个装饰器函数名,以此执行
16 
17 '''
18 from flask import Flask, views, render_template, send_file, request, session
19 
20 app=Flask(__name__)
21 app.secret_key='wertyui234567gf!@#$%^&*('
22 
23 class Login(views.MethodView):
24     # methods = ['get', 'post', 'head', 'options','delete', 'put', 'trace', 'patch']#可以在类中指定允许的请求方式,不指定会根据定义的函数进行选择
25     decorators = []#对类中的函数都加上装饰器,列表中可以放多个装饰器函数名,以此执行
26     def get(self):
27         return render_template('login.html')
28     def post(self):
29         username=request.form.get('username')
30         pwd=request.form.get('pwd')
31         if username == 'yang' and pwd == '123456':
32             session['username'] = 'yang'
33             return '登录成功!'
34         else:
35             return '账号或密码有误!!!!'
36 #路由映射视图函数
37 app.add_url_rule('/login',view_func=Login.as_view(name='login'))
38 
39 if __name__ == '__main__':
40     app.run()

lohin.html

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>logine</title>
 6 </head>
 7 <body>
 8 <form action="" method="post">
 9     用户名:<input type="text" name="username">
10     密码:<input type="password" name="pwd">
11     <input type="submit" value="提交">
12 </form>
13 </body>
14 </html>

 

posted on 2019-12-09 13:45  始终不够啊  阅读(321)  评论(0编辑  收藏  举报