Flask

2018-09-01 05:53:19来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

Flask攻略

python三大框架预览

Python Web框架分类
    功能分类:
        a:收发消息相关(socket)
        b:根据不同的URL执行不同的函数(业务逻辑相关的)
        c:实现动态网页(字符串的替换)

    Web框架分类:
        1、自己实现b,c,使用第三方的a(Django)
        2、自己实现b,使用第三方的a,c(Flask)
        3、自己实现a\b\c(Tornado)
两个模块
web服务器程序 《---WSGI协议---》 web应用程序

1、wsgiref
Django开发环境使用的就是wsgiref模块
2、jinja2(Flask)
渲染HTML页面,其实就是实现字符串的替换
python三大框架优缺点

  Flask:

    优点: 小而精,短小精悍,第三方组件特别多

    缺点: 组件更新速度取决于开源者 

  Tornado:

    优点: 原生的WebSocket, 异步任务, IO非阻塞玩

    缺点: 没有组件,Session都没有

  Django:

   优点: 大而全,组件非常全面

    缺点: 太大,重量级框架,加载太大,浪费资源

Flask的优势

Flask中的Werkzuge原理,__call__()
Flask的第一个Hello Flask
Flask的 HTTPresponse render_template redirect
Flask中的 request
Flask中的 Jinja2 ,Markup
Flask中的 session secret_key
Flask中的路由系统 url_for

Flask中Werkzuge原理

from werkzeug.wrappers import Response, Request
from werkzeug.serving import run_simple


# application源码里面 get_wsgi_headers 里面有个__call__方法
# 可以调用__call__ 在执行flk的时候执行call里面的结果
@Request.application
def flk(r):
    print(r.url)
    if r.url == '/123':
        asd()
    return Response("hello")

# flk
run_simple("127.0.0.1", 5500, flk)

def asd():
    print('kermit')

asd()

Flask三剑客

# HTTPresponse: return "Hello Flask"
# render_template: return render_template("login.html") #templates
# redirect: return redirect("/login")

示例:

# Httpresponse
@app.route("/index")
def index():
    return "Hello Flask"


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


# redirect
@app.route("/")
def root_path():
    return redirect("/login")

Flask需要自己开启:(流程如下)

from flask import Flask, render_template, redirect
app = Flask(__name__)
app.run()  # 可以自定义ip 端口  和debug模式

Flask的request

request.method 获取请求方式
request.form 获取FromData数据(通常情况下的POST请求)
request.args    获取GET方式提交的数据
request.files    获取file
request.url_about    获取所有的关于URL的参数
request.values    获取全部提交方式
to_dict    坑,覆盖,GET覆盖POST
request.headers    获取头部信息
request.cookie    获取客户端cookie
request.json    数据头:application/json
request.data    在没有任何数据头的情况提交的POST

Flask的模板语言

# Flask 基于 Jinja2 做了一层小的封装,向前端传递数据 render_template 参数传递 stu = {k1:v1}

{{ stu }}
    <td>{{ stu.name }}</td>
    <td>{{ stu["age"] }}</td>
    {% if stu.get("gender") == ""  %}
        <td>男</td>
    {% else %}
        <td>{{ stu.get("gender") }}</td>
    {% endif %}
    
    
    STUDENT_LIST = [
        {'name': 'stu1', 'age': 38, 'gender': ''},
        {'name': 'stu2', 'age': 73, 'gender': ''},
        {'name': 'stu3', 'age': 84, 'gender': ''}
    ]
    
    <td>{{ stu.0.name }}</td>
    <td>{{ stu[0]["age"] }}</td>
    {% if stu[0].get("gender") == ""  %}
        <td>男</td>
    {% else %}
        <td>{{ stu.0.get("gender") }}</td>
    {% endif %}


    @apl.template_global()
    def a_b_sum(a,b):
        return a+b
    {{ a_b_sum(123,234) }}
    
    @apl.template_filter()
    def a_b_c_sum(a,b,c):
        return a+b+c
    {{ 123 | a_b_c_sum(1,2) }}

注意:安全字符串,Markup相当于模板里面有safe

{{ input | safe }}
    Markup() : Markup("<input type='text' name='input_tag'>")

pycharm识别模板语言的格式:设置jinja2的语言

Session

secret_key = "" 这个是开启session必备的参数

form flask import session
    app.secret_key = "随意"
    session["user"] = "xxxx"
    if session.get("user")

Jsonify

json的转换兼容性比json模块强

from flask import jsonify
# import json
# Flask的jsonify是等同于json
# 区别是json的转换兼容性比json模块强
@flk.route('/json')
def get_json():
    res = {
        "user_id": 1,
        "username": "kermit",
        "password": "123"
    }
    # return json.dumps(res)
    return jsonify(res)

Flask路由

flk.route()

"/index"  路由地址
"/index/<nid>"   动态路由地址(视图函数需要nid参数)
"/index/<int:nid>"   动态路由地址

"/files/<filename>"     <> 里面的filename可以是本地文件内的任何一个文件全名,可以把任意存在的文件内容打印到页面上
# 可以把所有文件内容显示到页面上面
@flk.route('/files/<filename>')
def files(filename):
    return send_file(filename)

补充

methods=["GET","POST"]   允许URL的请求方式
endpoint="index"   反向URL操作,可以解决Inner重名的问题
redirect_to="/index2"   服务器端页面跳转  301永久性重定向
strict_slashes=False   可以使用"/"结尾 反之不可以
defaults={"nid":1}   视图函数默认参数

Flask配置

settings.FlaskSettings

DEBUG = True
app.config["secret_key"] = "xxxxx"
TESTING = True
{
    '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,
}
flask的整套配置项

蓝图(Blueprint)

from flask import Blueprint,render_template,redirect
reg = Blueprint("reg",__name__,template_folder="reg_temp",static_folder="regs",static_url_path="/regs")

@reg.route(
"/reg") def reg_user():   return render_template("reg_index.html")

from flask import Flask import reg_user app = Flask(__name__) app.register_blueprint(reg_user.reg) if __name__ == '__main__':   app.run("0.0.0.0", 9527, debug=True)

1.Flask实例配置
app.config.form_object("setting.FlaskSetting")
app.DEBUG = True 开启Debug模式,该完代码不用手动重启
app.SECRET_KEY = "xxxxx" 开启session必备参数


2.初始化配置(Flask,Blueprint)
template_folder="reg_temp",
static_folder="regs",
static_url_path="/regs"

3.蓝图Blueprint
在app实例中注册蓝图app.register_blueprint(reg_user.reg)
实例化蓝图对象reg = Blueprint("reg",__name__,template_folder="reg_temp",static_folder="regs",static_url_path="/regs")
蓝图对象同样与Flask对象,具备独立模板和静态目录

from flask import Blueprint, render_template, redirect
import DATA

student_list = Blueprint('student_list', __name__, static_folder="list_static", static_url_path="/list_static")


# 蓝图实例对象一定不能与视图函数同名
@student_list.route('/list')
def list():
    return render_template('index.html')

4.before_request after_request before_frist_request
before_request 在请求进入视图函数之前做出的处理
after_request 在视图函数返回给用户之前做出的处理
before_frist_request 在第一次请求进入视图函数之前做出的处理

# 类似于Django中间件的功能
@flk.before_request  # 在请求之前
def be1():
    print('在请求进入App之前做出处理')
    # 判断url是不是/login
    if request.path == "/login":
        print('当前访问的页面url是:', request.path)
        # True返回None不作任何操作往下走
        return None
    if session.get("user"):
        print('userSession存在,直接访问页面!')
        return None
    else:
        print('user不存在,跳转到login页面')
        return redirect("/login")


@flk.before_request
def be2():
    print('执行be2')


# 在请求之后
@flk.after_request
def af1(response):
    print('在此时执行了after1')
    return response


@flk.after_request
def af2(response):
    print('在此时执行了after2')
    return response


# 第一次请求前执行
@flk.before_first_request
def be_first():
    print('第一次请求')


# 报错发送的状态码显示的结果
@flk.errorhandler(404)
def error_page(arg):
    return "当前页面不存在!"

5.errorheadler(404)
def error_page(arg)
错误信息头

第一个Flask

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/08/27 17:19
# @Author  : MJay_Lee
# @File    : flask_demo_test.py
# @Contact : limengjiejj@hotmail.com


from flask import Flask # 导入Flask类

app = Flask(__name__) # 实例化Flask对象 app

@app.route("/") # app中的route装饰器
def index(): # 视图函数
    return "first flask app"

app.run("0.0.0.0",5000,debug=True) # 启动Flask Web 服务

Flask三剑客

Flask中的HTTPResponse

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

from flask import Flask

app = Flask(__name__)

@app.route('/') # 路由
def index(): # 视图
    return "Hello Flask" # HTTPResponse

Flask中的redirect

from flask import Flask,redirect

app = Flask(__name__)

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

@app.route('/redi') # app中的route装饰器,用来指定视图函数的URL地址
def redi(): # 视图函数
    return redirect('/') # redirect 跳转至 '/'

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

Flask中的render(render_template)

from flask import Flask,redirect,render_template

app = Flask(__name__)

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

@app.route('/redi')
def redi():
    return redirect('/')


@app.route("/home") # app中route装饰器,用来指定视图函数的url地址
def home(): # home 视图函数
    return render_template("home.html") # 渲染HTML模板发安徽HTML页面

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

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

 

Flask中的request

每个框架中都有处理请求或收发消息的机制(request),而每个框架都有异同

一个form表单post的提交方式

html文件

<form action="" method="post" novalidate>
    <input type="text" name="user" id="">
    <input type="password" name="pwd" id="">
    <input type="submit" value="提交">

py文件

@app.route("/home",methods=["GET","POST"]) # app中route装饰器,用来指定视图函数的url地址
def home(): # home 视图函数
    if request.method == "POST":
        print(request.method) # POST
        print(request.form) # ImmutableMultiDict([('user', 'lmj'), ('pwd', '123')])
        print(request.form['user']) # lmj
        print(request.form['pwd']) # 123
        print(list(request.form.keys())) # ['user', 'pwd']
        return "POST请求已受理"

    return render_template("home.html") # 渲染HTML模板

 methods=["GET","POST"]代表这个URL地址只允许请求的方式,是个列表

request的参数获取

那么request的参数获取的几个方式

html文件

<form action="/home?id=1&age=18" method="post" novalidate>
    <input type="text" name="user" id="">
    <input type="password" name="pwd" id="">
    <input type="submit" value="提交">

py文件

@app.route("/home",methods=["GET","POST"]) # app中route装饰器,用来指定视图函数的url地址
def home(): # home 视图函数
    if request.method == "POST":
        print(request.form) # form表单中的参数

        print('request.args'.center(50,"="))
        print(list(request.args)) # url中的参数

        print('request.values'.center(50,"="))
        print(list(request.values)) # 所有参数

        print('request.values.to_dict'.center(50,"="))
        print(request.values.to_dict()) # 将参数转为字典

        return "POST请求已受理"
    return render_template("home.html") # 渲染HTML模板发安徽HTML页面

结果

===================request.args===================
['id', 'age']
==================request.values==================
['id', 'age', 'pwd', 'user']
==============request.values.to_dict==============
{'user': 'lmj', 'pwd': '123', 'id': '1', 'age': '18'}

注意:

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

request.cookies

print(request.cookies)
"""
{
    'csrftoken': 'elPT8HW8Zw74NPSftY0suJEqbm3cOxEPl05f1oezHN61kikgqOgcuXWtiJCyhtuX', 
    'sessionid': 'cqfjxs0svrq2nqf9i4hdghdvqep6468w'
}
"""

request.headers,请求头中的参数

print(type(request.headers)) # <class 'werkzeug.datastructures.EnvironHeaders'>
print(request.headers)
'''
Host: 127.0.0.1:5500
Connection: keep-alive
Content-Length: 16
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Origin: http://127.0.0.1:5500
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://127.0.0.1:5500/home
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,und;q=0.8,en;q=0.7
Cookie: csrftoken=elPT8HW8Zw74NPSftY0suJEqbm3cOxEPl05f1oezHN61kikgqOgcuXWtiJCyhtuX; sessionid=cqfjxs0svrq2nqf9i4hdghdvqep6468w
'''

request.data,若处理不了的就变成字符串存在data里

可以request.data,json.loads同样可以拿到里面的数据

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

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

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

request.files,文件上传

遇到文件上传,request.files村的就是上传的文件,但Flask在这个文件的操作中加了封装

html文件

<form action="" method="post" enctype="multipart/form-data" novalidate>
    <input type="file" name="file">
    <input type="submit" value="提交">
</form>

py文件

@app.route("/home",methods=["GET","POST"]) 
def home():
    if request.method == "POST":
        print(request.files) # ImmutableMultiDict([('file', <FileStorage: '待整理内容.txt' ('text/plain')>)])
        print(request.files["file"]) # <FileStorage: '待整理内容.txt' ('text/plain')>
        my_file = request.files["file"]
        my_file.save("upload_file.txt") # 保存文件,里面可以写完整路径+文件名

        return "POST请求已受理"
    return render_template("home.html")

request的路径获取

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

request.json

前提是你得告诉是json数据

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

Flask中的jinja2和render_template

前端-普通数值

<table border="1px">
    <thead>
        <tr>
            <td>姓名</td>
            <td>年龄</td>
            <td>性别</td>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>{{ stu.name }}</td>
            <td>{{ stu["age"] }}</td>
            <td>{{ stu.get("gender") }}</td>
        </tr>
    </tbody>
</table>

效果

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

前端-【列表】

<table border="1px">
    <thead>
    <tr>
        <td>姓名</td>
        <td>年龄</td>
        <td>性别</td>
    </tr>
    </thead>
    <tbody>
    {% for stu in stu_list %}
        <tr>
            <td>{{ stu.name }}</td>
            <td>{{ stu["age"] }}</td>
            <td>{{ stu.get("gender") }}</td>
        </tr>
    {% endfor %}
    </tbody>
</table>

效果

前端-【字典】

<table border="1px">
    <thead>
    <tr>
        <td>姓名</td>
        <td>年龄</td>
        <td>性别</td>
    </tr>
    </thead>
    <tbody>
    {% for item in stu_dict %}
        <tr>
            <td>{{ stu_dict[item].name }}</td>
            <td>{{ stu_dict[item]["age"] }}</td>
            <td>{{ stu_dict[item].get("gender") }}</td>
        </tr>
    {% endfor %}
    </tbody>
</table>

效果

render_template中可传递多个关键字

利用 **{}字典的方式实现

py文件

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

jinja2 的高阶用法

后端文件

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)

还有一种方式就是在前端,加上 safe

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

jinja2  模板中执行函数

后端

def a_b_sum(a,b):
    return a + b


@app.route("/home",methods=["GET","POST"])
def home():
    return render_template("home.html",tag = a_b_sum)

前端

{{ tag }}
<br>
{{ tag(10,20) }}

效果

补充:定义全局函数,无需后端传递给前端,jinja2就可以直接执行的函数

后端

 

@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("/home",methods=["GET","POST"])
def home():
    return render_template("home.html")

 

前端

<!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>

jinja2 模板复用block

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

 同理于django的母版

jinja2 模板语言中的宏定义

前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h2>定义宏</h2>
{% macro type_text(name,type) %}
    <input type="{{ type }}" name="{{ name }}" value="{{ name }}">
{% endmacro %}
<br>
<h2>使用宏来生成input标签</h2>
{{ type_text("one","text") }}
{{ type_text("two","text") }}
</body>
</html>

效果:

Flask 第五篇幅

 flask-login与验证校验,待更新

https://www.cnblogs.com/minsons/p/8045916.html
参考笔记

Flask 的路由系统

route 装饰器中的参数

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

@app.route("/index",methods=["GET","POST"]) # 当前url地址允许访问的请求方式
def index():
    return render_template("index.html")

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

from flask import Flask,render_template,url_for

app = Flask(__name__)

@app.route("/index",methods=["POST","GET"],endpoint="url_info") # 反向url地址,默认为视图函数名 (url_for)
def index():
    print(url_for("url_info")) # /index
    return render_template("index.html")

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

defaults,视图函数的参数默认值

from flask import Flask,render_template

app = Flask(__name__)

@app.route("/index",defaults={"user_list":["lmj","cly"]})
def index(user_list):
    return render_template("index.html",user_list=user_list) # 视图函数的默认参数
app.run("0.0.0.0",9527,debug=True)

strict_slashes,url地址结尾符"/"的控制,默认为True(结尾必须不能是"/")

from flask import Flask,render_template

app = Flask(__name__)

@app.route("/index",strict_slashes=False) # 有效访问地址:/index 或 /index/
def index():
    return render_template("index.html")
app.run("0.0.0.0",9527,debug=True)

redirect_to,url地址重定向

from flask import Flask,render_template

app = Flask(__name__)

@app.route("/home")
def home():
    return "你已跳转至home页面"

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


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

subdomain,子域名前缀subdomain="mjlee"这样写就可以得到mjlee.dream.com

前提是app.config["SERVER_NAME"]="dream.com"

from flask import Flask,render_template

app = Flask(__name__)

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

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


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

# 访问地址为:mjlee.dream.com/index

动态参数路由

from flask import Flask,url_for

app = Flask(__name__)


# 访问地址 http://127.0.0.1:5500/index/9527
@app.route("/index/<int:nid>",endpoint="url_info")
def index(nid):
    print(url_for("url_info",nid=nid)) # /index/9527
    return f"success get by {nid}" # success get by 9527


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

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

但是这种动态参数路由,在url_for的时候,一定要将动态参数名+参数值添加进去(nid=nid),否则报错

补充:

另外还可使用正则路由,前提是正则玩的溜

Flask 的config

 Flask的灵活体现出自其config配置,首先展示一下:

from flask import Flask

app = Flask(__name__) # type:Flask

print(app.config)
print(app.config["APPLICATION_ROOT"])


# 默认情况下,config的28个key的详情:
{
    'DEBUG': False,  # 是否开启DEBUG模式
    'TESTING': False,  # 是否开启测试模式
    'PROPAGATE_EXCEPTIONS': None,  # 异常传播(是否在控制台打印LOG)
    'PRESERVE_CONTEXT_ON_EXCEPTION': None, 
    'SECRET_KEY': None,  # 启用Session的时候,一定要使用它
    'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31),  # days,Session的生命周期默认31天
    'USE_X_SENDFILE': False,  # 是否启用 x_sendfile
    'LOGGER_NAME': '__main__',  # 日志记录器的名称
    '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,  # cookie的路径
    'SESSION_COOKIE_HTTPONLY': True,  # 控制 cookie 是否应被设置 httponly 的标志
    'SESSION_COOKIE_SECURE': False,  # 控制 cookie 是否应被设置安全标志
    'SESSION_REFRESH_EACH_REQUEST': True,  # 这个标志控制永久会话如何刷新
    'MAX_CONTENT_LENGTH': None,  # 如果设置为字节数, Flask会拒绝内容大于此值的请求进入并返回403状态码
    'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(0, 43200),  # 12小时,默认缓存控制的最大期限
    '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
}

app.config["DEBUG"] = True

只要代码发生改动,自动重启Flask程序(app.run)

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

修改配置的方式:

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

app.config["DEBUG"]=True

2、使用类的方式导入

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

settings.py

class FlaskSetting:
    DEBUG = True
    SECRET_KEY = "lmj"

然后在Flask的启动文件中引用

from flask import Flask

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

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

app.run("0.0.0.0",9528)

这叫做类导入配置

以上是针对一个已经实例化的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)
    修改客户(blueprint)
    添加客户(blueprint)
    删除客户(blueprint)

第一个蓝图示例

目录结构

 

蓝图check_view.py中的内容

from flask import Blueprint


cv = Blueprint("cv", __name__)

@cv.route("/index")
def index():
    return "show page"

@cv.route("/home")
def home():
    return "home page"

主服务 manage.py 中的内容

from flask import Flask
from crm_views import add_view,check_view


app = Flask(__name__)

app.config["DEBUG"] = True

app.register_blueprint(check_view.cv)
app.register_blueprint(add_view.av)

app.run("0.0.0.0",5500)

访问http://127.0.0.1:5500/home的效果

结论:没有在Flask对象中添加路由,但是注册了有路由和视图函数的蓝图对象。

如何理解蓝图

接下来看看,在示例话蓝图的时候可以传递的参数都有什么

目录结构

check_view.py

from flask import Blueprint,render_template


cv = Blueprint("cv",
               __name__,
               template_folder="cv_templates", # 每个蓝图都可以独立出一套templates模版文件夹,若不写则默认共享项目根目录中的templates
               static_folder="cv_static" # 静态文件目录也是可以独立出来
               )

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

@cv.route("/home")
def home():
    return "home page"

cv_show.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>check view page</h2>
<img src="/cv_static/lmj.png" alt="yanyan bf" style="height: 100px">
</body>
</html>

http://127.0.0.1:5500/index访问效果

注意:蓝图内部的视图函数及route重复,则主服务则按照注册的顺序执行,先注册的则先执行!

蓝图制作增删改查

目录结构

student

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/8/30 上午10:30
# @Author  : MJay_LEE
# @File    : __init__.py.py
# @Contact : limengjiejj@hotmail.com


from flask import Flask
from student_select import stu_select
from student_add import stu_add
from student_edit import stu_edit
from student_del import stu_del


def create_app():
    app = Flask(__name__)
    app.config["DEBUG"] = True
    app.register_blueprint(stu_select.ss)
    app.register_blueprint(stu_add.sa)
    app.register_blueprint(stu_edit.se)
    app.register_blueprint(stu_del.sd)
    return app
__init__.py

student_add

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加学生</title>
</head>
<body>
<h2>添加学生</h2>
<form action="" method="post">
    <p><label for="id">id<input type="text" name="id"></label></p>
    <p><label for="name">name<input type="text" name="name"></label></p>
    <p><label for="age">age<input type="text" name="age"></label></p>
    <p><label for="gender">gender<input type="text" name="gender"></label></p>
    <p><input type="submit" value="添加学生"></p>
</form>
</body>
</html>
s_add.html
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/8/30 上午11:03
# @Author  : MJay_LEE
# @File    : stu_add.py
# @Contact : limengjiejj@hotmail.com


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


sa = Blueprint("sa",
               __name__,
               template_folder="html"
               )

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

        STUDENT.append({
            "id":id,
            "name":name,
            "age":age,
            "gender":gender
        })

        return redirect("/s_list")

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

student_del

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/8/30 下午4:23
# @Author  : MJay_LEE
# @File    : stu_del.py
# @Contact : limengjiejj@hotmail.com

from flask import Blueprint,redirect
from student_data import STUDENT

sd = Blueprint("sd",__name__)

@sd.route("/s_del/<int:id>")
def s_del(id):
    for key,stu in enumerate(STUDENT):
        if stu["id"] == id:
            STUDENT.pop(key)
    return redirect("/s_list")
stu_del.py

student_edit

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加学生</title>
</head>
<body>
<h2>学生修改</h2>
<form action="" method="post">
    <p><label for="id">id<input type="text" name="id" value="{{ stu.id }}"></label></p>
    <p><label for="name">name<input type="text" name="name" value="{{ stu.name }}"></label></p>
    <p><label for="age">age<input type="text" name="age" value="{{ stu.age }}"></label></p>
    <p><label for="gender">gender<input type="text" name="gender" value="{{ stu.gender }}"></label></p>
    <p><input type="submit" value="修改学生"></p>
</form>
</body>
</html>
s_edit.html
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/8/30 上午11:34
# @Author  : MJay_LEE
# @File    : stu_edit.py
# @Contact : limengjiejj@hotmail.com


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


se = Blueprint("se",__name__,template_folder="html")

@se.route("/s_edit/<int:id>",methods=["GET","POST"])
def s_edit(id):
    if request.method == "POST":
        new_id = int(request.form.get("id"))
        new_name = request.form.get("name")
        new_age = request.form.get("age")
        new_gender = request.form.get("gender")

        stu_dict = {
            "id":new_id,
            "name":new_name,
            "age":new_age,
            "gender":new_gender
        }

        for stu in STUDENT:
            if stu["id"] == id:
                stu.update(stu_dict)
        return redirect("/s_list")

    for stu in STUDENT:
        if stu["id"] == id:
            return render_template("s_edit.html",stu=stu)
    return render_template("s_edit.html",stu="")
stu_edit.py

student_select

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/8/30 上午11:34
# @Author  : MJay_LEE
# @File    : stu_edit.py
# @Contact : limengjiejj@hotmail.com


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


se = Blueprint("se",__name__,template_folder="html")

@se.route("/s_edit/<int:id>",methods=["GET","POST"])
def s_edit(id):
    if request.method == "POST":
        new_id = int(request.form.get("id"))
        new_name = request.form.get("name")
        new_age = request.form.get("age")
        new_gender = request.form.get("gender")

        stu_dict = {
            "id":new_id,
            "name":new_name,
            "age":new_age,
            "gender":new_gender
        }

        for stu in STUDENT:
            if stu["id"] == id:
                stu.update(stu_dict)
        return redirect("/s_list")

    for stu in STUDENT:
        if stu["id"] == id:
            return render_template("s_edit.html",stu=stu)
    return render_template("s_edit.html",stu="")
s_list.html
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/8/30 上午10:33
# @Author  : MJay_LEE
# @File    : stu_select.py
# @Contact : limengjiejj@hotmail.com


from flask import Blueprint,render_template
from student_data import STUDENT


ss = Blueprint("ss",
               __name__,
               template_folder="html",
               )

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

manager.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/8/30 上午10:31
# @Author  : MJay_LEE
# @File    : manager.py
# @Contact : limengjiejj@hotmail.com


from student import create_app


flask_app = create_app()


flask_app.run("0.0.0.0",9527)
manager.py

student_data.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/8/30 上午10:31
# @Author  : MJay_LEE
# @File    : student_data.py
# @Contact : limengjiejj@hotmail.com


STUDENT = [
    {"id":1,"name":"lmj","age":18,"gender":"male"},
    {"id":2,"name":"cly","age":23,"gender":"female"},
    {"id":3,"name":"festival","age":1,"gender":"male"},
]
student_data.py

before_request 与 after_request

现在模拟一个简单的场景

一个Flask程序,有4个路由和视图函数,做登录校验

如果登录了,就可以访问index和home页面

若没有登录就跳转到login登录

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/8/31 上午10:45
# @Author  : MJay_LEE
# @File    : main.py
# @Contact : limengjiejj@hotmail.com

from flask import Flask,request,redirect,session,render_template


app = Flask(__name__)
app.secret_key = "limengjie"


# 在请求(request)之前作出响应
@app.before_request
def is_login():
    if request.path == "/login":
        return None

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


# 在响应(response)之前作出响应
@app.after_request
def foot_log(environ):
    if request.path != "/login":
        print("有访客访问了",request.path)
    return environ


@app.route("/login",methods=["GET","POST"])
def login():
    if request.method == "POST":
        user = request.form.get("user")
        if user == "lmj":
            session["user"] = 1
            return redirect("/index")
    return render_template("login.html")


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


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


@app.route("/logout",methods=["GET","POST"])
def logout():
    if request.method == "POST":
        session["user"] = None
        return "注销成功"
    return render_template("logout.html")


app.run("0.0.0.0",9527,debug=True)
4个路由和视图函数的栗子

注意:session一定要配合secret_key一起使用

 

理论引导实践,欲求精通还需多加刻苦实践!祝君好运! 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:python进程-进阶

下一篇:Python IF 条件判断