新篇章---flask---2

一 flask中的路由系统

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

1.1.0 首先,回顾装饰器写法:

def warpper(f):
    def inner(*args,**kwargs):
        print(123)
        ret = f(*args,**kwargs)
        return ret
    return inner
    
@warpper
def f():
    print(666)
    return
    
f()

1.1.1 methods:

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

from flask import Flask,redirect,request

app = Flask(__name__)

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

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

此时,输入url是http://192.168.16.18:5000/index?id=2,此时得到页面显示Hello boy 2.

1.1.2 endpoint(别名,参考django的反向解析)

endpoint:
概念:就是id(Identifier),类似django中url的别名(name=...),拿到反向url地址,默认是视图函数名,也可以自己修改
好处:使用endpoint后,我们可以随意改变应用中的URL地址,却不用修改与之关联的资源的代码。
过程:viewfunction–>endpoint–>URL
采用该过程的原因(不跳过endpoint):
原因就是采用这种方法能够使程序更高、更快、更强。例如蓝图。蓝图允许我们把应用分割为一个个小的部分,对于不同的应用, 蓝本允许把应用分割为一个个以命名空间区分的小部分
(参考第二个实例)

理解一个小代码:

@app.route('/user/<name>')
def user(name):
    return 'Hello, %s' % name

该函数等价于

def user(name)
    return 'Hello, %s' % name
    
app.add_url_rule('/user/<name>', 'user', user)

add_url_rule有四个参数,不考虑options,前三个参数分别是rule,view_func,endpoint.

rule:匹配的路由地址.
view_func:我们正常写的视图函数.
endpoint:上面的那些了.

url_for是什么?---简单来说,url_for()是获取当前的url地址.里面需要的参数是端点(endpoint值),

补充:(里面的参数与解释(机器翻译))
:endpoint:URL的端点(函数名)。
:values:URL规则的变量参数。
: _external:如果设置为True,则生成绝对URL。伺服器。
可以通过SERVER_NAME配置变量更改地址,
回退到Host头部,然后返回到请求的IP和端口。
:_scheme:指定所需URL方案的字符串。_外部。参数必须设置为True或引发:exc:ValueError。缺省值。行为使用与当前请求相同的方案,或首选URL_SCHEME来自:ref:app configuration<config>if no。请求上下文可用。从Werkzeug 0.10开始,也可以设置。设置为空字符串以构建协议相关URL。
:_anchor:如果提供,则将其作为锚点添加到URL中。
:_method:如果提供,则显式指定HTTP方法。

端点(endpoint)就是程序中一组逻辑处理单元的ID,该ID对应的代码决定了对此ID请求应该作出何种响应。通常,端点与视图函数同名,但是你也可以修改它.
URL (http://www.example.org/greeting/Mark) 映射到端点"say_hello"上. 指向端点"say_hello"的请求被视图函数"give_greeting"处理.
端点通常用作反向查询URL地址(viewfunction–>endpoint–>URL)。例如,在flask中有个视图,你想把它关联到另一个视图上(或从站点的一处连接到另一处)。不用去千辛万苦的写它对应的URL地址,直接使用URL_for()就可以啦

参考链接1
参考链接2
参考链接3---的用法---前后数据交互

1.1.2.1 应用实例1(参考代码模板)

from flask import Flask, request, url_for

app = Flask(__name__)
app.config['DEBUG'] = True


@app.route('/index', methods=["POST", "GET"], endpoint="r_info")
def student_info():
    print(url_for('r_info'))
    stu_id = int(request.args["id"])
    return f"hello boy {stu_id}"


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

1.1.2.2 应用实例2(读书人的事,能说偷吗)

main.py

from flask import Flask, Blueprint
from admin import admin
from user import user

app = Flask(__name__)
app.register_blueprint(admin, url_prefix='admin')
app.register_blueprint(user, url_prefix='user')

admin.py

admin = Blueprint('admin', __name__)

@admin.route('/greeting')
def greeting():
    return 'Hello, administrative user!'

user.py

user = Blueprint('user', __name__)
@user.route('/greeting')
def greeting():
    return 'Hello, lowly normal user!'

1.1.2.3 对url_for的补理解和补充

概念:
url_for()函数用于构建指定函数的url.
url_for操作对象是函数(准确来说是通过endpoint操作view_func),而不是route里面的路径,返回的是路径(可操作自己想返回的路径(绝对/相对/自己拼接))
url_for()可以直接用view_func名进行查询,也可以用endpoint进行查询(实际上view_func名就是默认的endpoint(如果不进行修改的话))
使用url_for()的原因:
将来如果修改了'URL',但是没有修改URL对应的函数名,就不用到处去替换URLl
'url_for'会自动的处理那些特殊的字符,不需要去手动处理

url_for()的使用范例:

from flask import Flask, url_for

app = Flask(__name__)

# We can use url_for('foo_view') for reverse-lookups in templates or view functions
@app.route('/foo')
def foo_view():
    pass

# We now specify the custom endpoint named 'bufar'. url_for('bar_view') will fail!
@app.route('/bar', endpoint='bufar')
def bar_view():
    pass

with app.test_request_context('/'):
    print (url_for('foo_view'))  #/foo
    print (url_for('bufar'))  #/bar
    # url_for('bar_view') will raise werkzeug.routing.BuildError
    print (url_for('bar_view'))  #端点bar_view是没有定义的

参考链接1

1.1.2.4 最终的代码(自己写的)

from flask import Flask, request, url_for

app = Flask(__name__)
app.config['DEBUG'] = True


@app.route('/index', methods=["POST", "GET"], endpoint="r_info")
def student_info():
    print(url_for('r_info',name='Mark',id=2))
    #写name=Mark就会接到后面,`/index?name=Mark`,我们可以通过这种方式传递参数 ,
    # 加上id=2就变成了`/index?name=Mark&id=2`
    stu_id = int(request.args["id"])
    return f"hello boy {stu_id}"

@app.route('/index/<page>')
def my_list(page):
    return f'{page}'

if __name__ == '__main__':
    app.run('0.0.0.0', 5000, debug=True)
url为`http://192.168.16.18:5000/index?id=3`(如果不写上id=3,怎么可能能接到呢?你写在url_for中只是为了传给前端,而前端写?id=3是为了从前端传输给后端,两个根本不同!!!)

1.1.3 defaults

defaults:视图函数的参数默认值(能给视图函数写入形参的方式之一)(另一种是用类似的情况获取)

from flask import Flask, request, url_for

app = Flask(__name__)
app.config['DEBUG'] = True

@app.route('/index/<page>',methods=['POST','GET'],defaults={'page':123})
def my_list(page):
    return f'{page}'

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

输入url为http://192.168.16.18:5000/index/12,此时显示123,并不显示12,说明使用的是默认的数值.(不能不输入12,不输入的话是404)
注意,default进行传值的时候要用字典dict.

1.1.4 strict_slashes

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

使用地方:

from flask import Flask, request, url_for

app = Flask(__name__)
app.config['DEBUG'] = True

@app.route('/index/<page>',methods=['POST','GET'],strict_slashes=True)
def my_list(page):
    return f'{page}'

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

测试发现,True的时候加上/就是not found...

1.1.5 redirect_to:url地址重定向

没什么好说的,308永久重定向

from flask import Flask, request, url_for

app = Flask(__name__)
app.config['DEBUG'] = True

@app.route('/index/<page>',methods=['POST','GET'],redirect_to='/index')
def my_list(page):
    return f'{page}'

@app.route('/index')
def youxiu():
    return 'sgoyi'

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

输入http://192.168.16.18:5000/index/12,跳转到http://192.168.16.18:5000/index.

1.2 动态参数路由

比如返回一个文件内容(如果返回其他目录的内容的话记得用os.path.join进行拼接)

from flask import Flask, request, url_for,send_file

app = Flask(__name__)
app.config['DEBUG'] = True


#如何返回一个文件内容
@app.route('/index/<filename>',methods=['POST','GET'])
def my_list(filename):
    print(filename)
    return send_file(filename)


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

结果返回的是这个文件内容.

1.3 补充(Markup)

如果防止HTML显示为字符串,flask采用Markup实现,而django采用make_safe。

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd

from flask import Flask,render_template
app = Flask(__name__)
@app.route('/index')
def index():
return render_template('index.html')


@app.template_global() #simple_tag
def foo(arg):
return "<input type='text' value='{}'>".format(arg)

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

此外,Flask的Jinjia2可以通过Context 把视图中的函数传递把模板语言中执行,这就是Django中的simple_tag和simple_fifter功能。simple_tag(只能传2个参数,支持for、if)

二 实例化flask的参数和对app的配置

2.1

  1. Config对象配置(重点:快速切换debug,testing,线上环境配置)

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 目录

三 蓝图

3.1 概念

蓝图(blueprint):作用是将功能和主服务分开.
蓝图除了run,跟主程序差不多(有点像程序).内部的视图函数和route不要重复...(注意问题哦)

蓝图的目录结构:
蓝图的目录结构

3.2 步骤

  1. 第一步,创建一个项目并且把目录结构做成自己想要的结构...(考虑蓝图,settings配置)
  2. 在s.py文件里写蓝图.
  3. 导入到主程序中.

明显的我们没有在Flask对象中添加路由,但是我们注册了有路由和视图函数的sv蓝图对象
(尝试下蓝图的增删改查吧!!!)
蓝图的增删改查

3.3 蓝图代码

manage.py

from flask import Flask

#导入写好的蓝图模块
from blueprint.tester import bp1,bp2

app = Flask(__name__)
app.config['DEBUG'] = True

app.register_blueprint(bp1)
app.register_blueprint(bp2)

@app.route('/')
def te():
    return {1:'234天下'}

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

在blueprint的tester.py中有

from flask import  Blueprint

bp1 = Blueprint('bp1',__name__)

@bp1.route('/bp1')
def test1():
    return '时间过的好快啊'

bp2 = Blueprint('bp2',__name__)
@bp2.route('/bp2')
def test2():
    return {'人为何能咸鱼':'因为有自知之明'}

其中,bp1和bp2就是蓝图.(可以把它看成一个py文件就是一类,里面写的东西都是一个性质的)

四 特殊的装饰器

4.1 before_request(在请求(request)进入视图函数之前执行)

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

# 导入写好的蓝图模块
from blueprint.viewer import login, index

app = Flask(__name__)
app.config['DEBUG'] = True
app.secret_key = 'asdfghjkjhrge'


# app.register_blueprint(login)
# app.register_blueprint(index)

# 特殊装饰器
@app.before_request
def is_login():
    if request.path == '/login':
        return None
    if not session.get('uname'):
        return redirect('login')


@app.route('/login', methods=["POST", 'GET'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        uname = request.form.get('username')
        pwd = request.form['password']
        if uname == '123' and pwd == '123':
            session['uname'] = uname
            return redirect('index')
        else:
            return redirect('login')


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


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

还有after_request.

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

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

这个很少有人用的哈...但要了解哦.

4.3 404页面(同属特殊的装饰器)

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

# 导入写好的蓝图模块
from blueprint.viewer import login, index

app = Flask(__name__)
# app.config['DEBUG'] = True
# app.secret_key = 'asdfghjkjhrge'


# app.register_blueprint(login)
# app.register_blueprint(index)

# 类导入配置(注意使用app.default_config(如果忘记了的话))
from config_mode.settings import DebugSettings, TestSettings

app.config.from_object(DebugSettings)


# app.config.from_object(TestSettings)


# 特殊装饰器
@app.before_request
def is_login():
    if request.path == '/login':
        return None
    if not session.get('uname'):
        return redirect('login')


# 报错信息(404的)
@app.errorhandler(404)
def error404(error_message):
    print(error_message)
    return redirect(
        'https://gitee.com/python_stack_20/teaching_plan/issues/IZ16D%E6%92%92%E6%89%93%E9%A3%9E%E6%9C%BA%E8%80%83%E6%A0%B8%E6%B3%95%E7%AC%AC%E4%B8%89%E6%96%B9%E7%9A%84%E8%90%A8%E5%87%A1%E7%BA%B3%E7%9A%84v')


@app.route('/login', methods=["POST", 'GET'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        uname = request.form.get('username')
        pwd = request.form['password']
        if uname == '123' and pwd == '123':
            session['uname'] = uname
            return redirect('index')
        else:
            return redirect('login')


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


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

五 问题

    if blueprint.name in self.blueprints:
AttributeError: 'function' object has no attribute 'name'

如果出现这个问题的时候,一般情况是蓝图的名字和主程序的名字一致造成的.

正确实例:(注意,实例化的蓝图名字不能和函数名相同,这就是上面问题的根源)

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

login1 = Blueprint('login1', __name__)


@login1.route('/login', methods=["POST", 'GET'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        uname = request.form.get('username')
        pwd = request.form['password']
        if uname == '123' and pwd == '123':
            session['uname'] = uname
            return redirect('index1')
        else:
            return redirect('login1')


index1 = Blueprint('index1', __name__)


@index1.route('/index')
def index():
    return render_template('index.html')

之前写错的代码:

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

login1 = Blueprint('login1', __name__)


@login1.route('/login', methods=["POST", 'GET'])
def login1():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        uname = request.form.get('username')
        pwd = request.form['password']
        if uname == '123' and pwd == '123':
            session['uname'] = uname
            return redirect('index1')
        else:
            return redirect('login1')


index1 = Blueprint('index1', __name__)


@index1.route('/index')
def index1():
    return render_template('index.html')

六 最终代码

views.py

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

login1 = Blueprint('login', __name__)


@login1.route('/login', methods=["POST", 'GET'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        uname = request.form.get('username')
        pwd = request.form['password']
        if uname == '123' and pwd == '123':
            session['uname'] = uname
            return redirect('index')
        else:
            return redirect('login')


index1 = Blueprint('index', __name__)


@index1.route('/index')
def index():
    return render_template('index.html')

settings.py

class DebugSettings(object):
    DEBUG = True
    SECRET_KEY = 'adsfhkjhgf'
    SESSION_COOKIE_NAME = 'I am debug session'

class TestSettings(object):
    TESTING = True
    SECRET_KEY = 'asfsafghgjhkhgsfad'
    SESSION_COOKIE_NAME = 'I am Not session'

s1.py

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

# 导入写好的蓝图模块
from blueprint.viewer import login1, index1

app = Flask(__name__)
# app.config['DEBUG'] = True
# app.secret_key = 'asdfghjkjhrge'


# 类导入配置(注意使用app.default_config(如果忘记了的话))
from config_mode.settings import DebugSettings, TestSettings

app.config.from_object(DebugSettings)


# app.config.from_object(TestSettings)


# 特殊装饰器
@app.before_request
def is_login():
    if request.path == '/login':
        return None
    if not session.get('uname'):
        return redirect('login')


# 报错信息(404的)
@app.errorhandler(404)
def error404(error_message):
    print(error_message)
    return redirect('https://gitee.com/python_stack_20/teaching_plan/issues/IZ16D%E6%92%92%E6%89%93%E9%A3%9E%E6%9C%BA%E8%80%83%E6%A0%B8%E6%B3%95%E7%AC%AC%E4%B8%89%E6%96%B9%E7%9A%84%E8%90%A8%E5%87%A1%E7%BA%B3%E7%9A%84v')


# @app.route('/login', methods=["POST", 'GET'])
# def login():
#     if request.method == 'GET':
#         return render_template('login.html')
#     else:
#         uname = request.form.get('username')
#         pwd = request.form['password']
#         if uname == '123' and pwd == '123':
#             session['uname'] = uname
#             return redirect('index')
#         else:
#             return redirect('login')
#
#
# @app.route('/index')
# def index():
#     return render_template('index.html')

app.register_blueprint(login1)
app.register_blueprint(index1)

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

七 之前的问题的补充

7.1 对装饰器的补充

解决一个装饰器用于多个函数的装饰的方法.
方法一:加入endpoint=函数名(避免重复,都是inner)
方法一
方法二:(导入warps模块,使用的时候避免都用inner作为函数名)
方法二

7.2 关于flask直接返回字典的问题

可以直接返回字典,但仅限于ASCII码的数据,也就是说,你输入中文不识别的!!!

posted on 2019-07-11 23:02  流云封心  阅读(61)  评论(0)    收藏  举报