flask之路由-视图-请求-响应

Flask

web服务依赖于第三方的一个wsgi模块:werkzeug

先看一个werkzeug的例子:
from werkzeug.wrappers import Request, Response

@Request.application
def hello(request):
    return Response('Hello World!')

if __name__ == '__main__':
    from werkzeug.serving import run_simple  # 启动socket的
    run_simple('localhost', 4000, hello)  # 本地4000端口
MVC架构

M:数据相关

V:数据的显示和呈现,view是对用户的直接输出

C:收集用户的输入,处理用户交互

安装:pip install Flask

参数配置
启动时,在run()中可以加参数:
1、debug=True:是否开启调试模式,开启后修改代码后会重启
2、threaded:是否开启多线程
3、port=9000:默认端口是5000,可以改
4、host=ip:默认127.0.0.1,可以改为0.0.0.0
app.run(debug=True,port=9000)

1、插件、扩展库

1、帮助开发者快速实现某种功能
2、下载安装,初始化配置
flask-script插件:可以实现命令行参数接收
安装:pip install flask-script
调整代码:
	from flask_script import Manager
	app = Flask(__name__)
	manage = Manager(app=app)
    manage.run()
# 装了这个插件,对代码做了修改后,不能直接启动了,需要在命令行启动,先看一些参数。
	-d:是否开启调试模式
    -r:是否自动重新加载文件
    -h:指定主机ip
    -p:指定端口
    --threaded:是否使用多线程
    -?:查看帮助信息
# 启动命令:python3 app.py runserver -r -d

2、flask代码结构

static:静态资源文件
templates:模板文件
app.py:视图函数
# flask默认是没有带models的

3、模板渲染

render_template():加载和渲染
也可以使用:
	template=Template("<h2>呵呵</h2>")
    template.render()
静态使用:相当于django的反向解析
url_for('static',filename='hello.css')
模板渲染示例
1、在static下新建css目录
2、在template目录下新建一个hello.html文件
<head>
    <meta charset="UTF-8">
    <title>hello</title>
    <link rel="stylesheet" href="/static/css/hello.css">
</head>
<body>
<h2>今天真是头大</h2>
<p>讲了一天的linux命令,没有长进!</p>
</body>
</html>
3、在app.py中写视图
from flask import Flask, render_template
from flask_script import Manager

app = Flask(__name__)  # 实例化传参只是给对象起名

manage = Manager(app=app)
# 使用此插件后,不能直接启动,需要:python36 app.py runserver -d -r启动

@app.route('/')
def hello_world():
    return 'Hello World!'

@app.route('/hello/')  # 路由
def hello():
    # 渲染模板
    return render_template('hello.html')

if __name__ == '__main__':
    # app.run(debug=True)
    manage.run()
关于用户登录的程序
# -*- coding: utf-8 -*-
from flask import Flask, render_template, request, redirect
from flask import session

# 有个template_folder参数,可修改模板目录名称
# static_folder:静态文件夹
# static_url_path:可以指定,然后在模板中路径就写这个,不写就是static
app = Flask(__name__)
# session存在其浏览器的cookie中,服务端这边加密返回
app.secret_key = 'asdfghjkl'

@app.route('/login/', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('user')
        password = request.form.get('pwd')
        if username == 'shj' and password == '123':
            # 保存session,记录用户登录状态
            session['user'] = username
            return redirect('/index/')
        else:
            return render_template('login.html', error='用户名或密码错误')
            # return render_template('login.html', **{'error': '用户名或密码错误'})
    return render_template('login.html')


@app.route('/index/')
def index():
    # 判断是否登录,session的默认时间是31天过期
    user = session.get('user')
    if not user:
        return redirect('/login/')
    return '欢迎使用'

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

# 以下是form表单
<h2>用户登录</h2>
<form action="" method="post">
    用户名:<input type="text" name="user">
    密码:<input type="password" name="pwd">
                                        {#模板渲染#}
    <input type="submit" value="提交">{{ error }}
</form>

4、flask配置文件

4.1、首先看配置文件导入的原理
'''给一个路径"settings.Foo",可以找到类并获取到其中的静态字段'''
# 新建一个settings.py文件
class Foo:
    DEBUG=True
    TEST=True

# 在任意文件中
import importlib

path = "settings.Foo"
p, c = path.rsplit('.', maxsplit=1)
m=importlib.import_module(p)
cls = getattr(m, c)
print(cls)  # 找到类
for key in dir(cls):
    if key.isupper():
        print(key)
        print(getattr(cls, key))
4.2、配置文件:app.config
# print(app.config)

# 方式一:直接在app.py中修改配置文件:
app.config['DEBUG']=True

# 方式二: 
# 但是都在里面修改太麻烦,可以写如下一句,然后自己新建一个配置文件,新建一个Foo类,在类中写:DEBUG=True之类的配置
app.config.from_object('settings.Foo')

# 方式三:跟方式二差不多,只是我在settings.py中写两个类(dev/prod),然后在app.py中修改即可。这样还有方便之处就是同样的配置可以抽出来一个公有类
class Base(object):
    xx=123
    
class Prod(Base):
    DEBUG=False
    
class Dev(Base):
    DEBUG=True

5、flask的view

请求流程:

​ 浏览器>route>views>models>views>templates>浏览器

路由中的配置

@app.route('/inde/as/fd/', methods=['GET', 'POST'], endpoint='n1')
def index():
    print(url_for('n1'))
    return 'Index'
# endpoint相当于django的name,做反向解析;如果不写默认等于函数名

路由参数

​ 1、路径参数

​ 2、请求参数(get的跟在路径的?后面;POST跟在body中)

flask中都是关键字参数,默认标识符是<参数名>

1、参数必须和视图函数中的对应一致
2、如果视图中的参数有默认值,在路由中可以不传
3、默认的参数类型是字符串
4、参数语法:<converter:var>即<类型:变量名>
converter的类型
string:接收任何没有斜杠('/')的文件  # (默认都是string)
int:接收整型,指定了int后,路由中必须输入的是数字,否则无法匹配
float:接收浮点型
path:接受路径,接收到的是字符串。它会把路由中的'/'当作普通字符
uuid:唯一码
any:可以同时指定多种路径进行限定。如:
	app.route('/any/<any(c,d,e):an>')
	路由只能在c,d,e中
route的规则
请求方法不像django全部支持,需要自己配置
# GET  POST  HEAD  DELETE  PUT
请求方法:
@app.route('/rule/', methods=['GET', 'POST'])
def hello():
    return 'hello'

#url_for反向解析,根据函数名,获取反向路径
url_for('函数名', 参数名=value)
反向解析及路由示例
from flask import Flask, render_template
from flask_script import Manager
import uuid

app = Flask(__name__)

manage = Manager(app=app)
# 使用此插件后,不能直接启动,需要:python36 app.py runserver -d -r启动

@app.route('/')
def hello_world():
    return 'Hello World!'


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

# 指定路由参数必须为uuid
@app.route('/get/<uuid:id>/')
def get(id):
    print(id)
    print(url_for('get', id=id))
    return '获取uuid参数'


# 获取uuid
@app.route('/getuuid/', methods=['GET'])
def get_uuid():
    return str(uuid.uuid4())

if __name__ == '__main__':
    # app.run(debug=True)
    manage.run()
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>hello</title>
    <link rel="stylesheet" href="/static/css/hello.css">
</head>
<body>
<h2>今天真是头大</h2>
<p>讲了一天的linux命令,没有长进!</p>
    <!-- 此处进行反向解析,获取uuid -->
<a href="{{ url_for('get_uuid') }}">获取uuid</a>
</body>
</html>

6、request

flask中的request是内置的对象,使用时需要导包

request方法集锦
from flask import request
request.method  # 请求方法
request.data  # 数据:bytes类型
request.args  # GET请求的参数:{{('name':'shj),('name':'cr)}}
request.form  # POST请求相关的数据,通过form表单提交
obj = request.files  # 文件
obj.save('/var/www/uploads/'+secure_filename(f.filename))
request.cookies  #post提交时,会有一个 {'csrf_token':xxxxxxxxxxx}
request.headers  # 请求头
request.json
request.remote_addr  # 远端ip
request.user_agent  # 浏览器身份
request.host  # 主机
request.url  # 访问路径

其中,args、form、files拿到的是类似字典的数据结构ImmutanleMultidict的对象,与字典区别就是可以有相同的键。

数据的获取方法
request.args.get('name')  # 如果参数中有两个name,取到的值是第一个键
request.args['name']  # 键不存在时会报错
request.args.getlist('name')  # 可以拿到对应key的所有值

7、response

视图函数返回接收两种类型:

​ 1、Response对象

​ 2、字符串:针对字符串,会自动帮我们封装成Response对象

返回的内容:

​ 1、返回模板:render_template(),可以加多返回一个状态码,反爬

​ 2、返回make_response(data,code)

​ 3、返回Response(文本内容,状态码)

​ 4、返回json格式

7.1、返回模板
@app.route('/hello/')
def hello():
    return render_template('hello.html'),401
# 返回一个状态码401做反爬:数据正常,但是返回一个错误状态码
7.2、返回make_response(data,code):定制响应头和cookie
from flask import Flask, render_template, make_response

# 第一种
@app.route('/response/')
def resp():
    response = make_response(render_template('hello.html'))
    return response

# 第二种:可以定制响应头,cookie
@app.route('/response/')
def resp():
    # 返回一个错误状态码,可在页面调试中查看
    response = make_response('<h3>刘志鹏,能成大事</h3>', 400)
    response.headers['xxxxxx'] = '123'  # 可以定制响应头
    response.set_cookie('key', 'value')  # 设置cookie
    return response
7.3、返回Response(文本内容,状态码)
from flask import Flask,Response

@app.route('/response/')
def resp():
    response = Response(response='<h3>刘志鹏,能成大事</h3>', status=403)
    return response
7.4、返回json格式
# 直接导入flask中的json即可
from flask import json

# 第一种——Content-Type: application/json
@app.route('/json/')
def get_json():
    # res = json.jsonify({'name': 'shj'})
    res = json.jsonify(name='shj', age=18)  # 可以这么传值
    return res
访问页面,发现页面拿到的数据类型是:application/json。即json.jsonify()将数据格式化为了json格式。

# 第二种,使用json.dumps(),使用这个前端拿到的是——Content-Type: text/html;
@app.route('/json/')
def get_json():
    res = json.dumps({'name': 'shj'})
    return res

# 第三种
from flask import Response,json

@app.route('/json/')
def get_json():
    res = '{"name": "shj", "age": 18}'
    response = Response(response=res, content_type='application/json', status=403)  # 指定返回的数据类型
    return response
7.5、重定向:redirect()
from flask import redirect,url_for,render_template

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

@app.route('/redirect/')
def red():
    # 反向解析:url_for('函数名',参数=value)
    return redirect(url_for('hello'))  
7.6、终止执行

不管对方如何访问,直接报错

# 终止一切访问
from flask import abort

@app.route('/abort/')
def ab():
    abort(403)
代码示例:学生管理

settings.py

class Base(object):
    SECRET_KEY='asdfghjkl'


class Prod(Base):
    DEBUG = False


class Dev(Base):
    DEBUG = True

app.py

# -*- coding: utf-8 -*-
from flask import Flask,redirect,url_for
from flask import render_template
from flask import session,request
from functools import wraps

app = Flask(__name__)
app.config.from_object('settings.Dev')

# 假定这是数据库的所有数据
STUDENT_DICT = {
    1: {'name': 'shj', 'age': 18, 'gender': 'male'},
    2: {'name': 'cr', 'age': '16', 'gender': 'female'},
}

# 登录认证装饰器
def auth(func):
    @wraps(func)
    def inner(*args, **kwargs):
        if not session.get('user'):
            return redirect(url_for('login'))
        res = func(*args, **kwargs)
        return res
    return inner

# 登录
@app.route('/login/', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('user')
        password = request.form.get('pwd')
        if username == 'shj' and password == '123':
            # 保存session,记录用户登录状态
            session['user'] = username
            return redirect('/index/')
        else:
            return render_template('login.html', error='用户名或密码错误')
    return render_template('login.html')

# 主页
@app.route('/index/')
def index():
    return render_template('index.html', stu_dic=STUDENT_DICT)

# 删除学生
@app.route('/delete/<int:nid>')
@auth
def delete(nid):
    STUDENT_DICT.pop(nid)
    # 反向解析
    return redirect(url_for('index'))

# 学生详情
@app.route('/detail/<int:nid>')
@auth
def detail(nid):
    info = STUDENT_DICT[nid]
    return render_template('detail.html', info=info)

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

'''
以上代码虽然实现了登录认证,但是每个需要认证的地方都需要加一个装饰器,造成代码冗余。
我们一般给某个函数加功能时才会用到装饰器,而现在批量做认证需要用到一个before_request,它装饰的函数在其他函数执行之前执行。每个请求进来都会经过它。如果它有返回值,那么就不会再往后执行了,直接返回结果。
'''
# 所以我们可以加一个before_request装饰的函数
@app.before_request
def auth():
    if request.path == '/login':
        return None  # 代表可以通过执行,如果返回其他就终止
    if session.get('user'):
        return None
    return redirect('/login')

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
    <h1 class="text-center">学生列表</h1>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <table class="table-hover table table-bordered">
                    <thead>
                        <tr>
                            <th>id</th>
                            <th>name</th>
                            <th>age</th>
                            <th>gender</th>
                            <th>选项</th>
                        </tr>
                    </thead>
                    <tbody>
                        {% for k,v in stu_dic.items() %}
                        <tr>
                            <td>{{ k }}</td>
                            {#  可以这么渲染:{{ v.get('name', '默认值')}} 或者  {{v['name']}} }}                        #}
                            <td>{{ v.name }}</td>
                            <td>{{ v.age }}</td>
                            <td>{{ v.gender }}</td>
                            <td>
                                <a href="/detail/{{ k }}">查看详情</a>
                                <a href="/delete/{{ k }}">删除</a>
                            </td>
                        </tr>
                        {% endfor %}
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</body>
</html>

detail.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h2>学生详情</h2>
<ul>
    {% for foo in info.values() %}
        <li>{{ foo }}</li>
    {% endfor %}

</ul>
</body>
</html>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值