Flask入门到实战

文章目录


我的网站: https://pythoneers.cn

1. Flask初识
1. Flask概述

Flask框架是一个短小精悍且可扩展强的Web框架,同Django框架一样依赖于第三方实现socket模块。

Flask特有上下文管理机制!

2. wsgi

WEB服务网关接口,是一个协议。Flask 中实现该协议的模块有 wsgirefwerkzeug,模块本质上就是 socket服务端用于接收客户端的请求并处理

面试中可能会被用到wsgi是什么?

3. 安装Flask

一般使用 pip 安装 Flask:pip install Flask,如果想加速下载,可以指定国内阿里云 pipy 的源,安装方法是:pip install flask -i https://mirrors.aliyun.com/pypi/simple

4. werkzurg

Flask 中实现 wsgi 的模块是 werkzurg,它是Flask第三方模块。

from werkzeug.serving import run_simple

def run(environ, start_response):
    return 'Hello Flask!'
    
if __name__ == '__main__':
    run_simple('localhost', 5000, run)
from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple

@Request.application
def hello_flask(request):
    return Response('Hello Flask!')
    
if __name__ == '__main__':
    run_simple('localhost', 5000, hello_flask)

Django中实现wsgi的模块是wsgiref

5. 第一个Flask应用
from flask import Flask

app = Flask(__name__)  # 实例化Flask类

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

if __name__ == '__main__':
    app.run()  # run方法启动socket
2. 配置文件的使用
1. 配置文件导入原理

给一个类的字符串路径,找到类并且获取类中大写的静态字段。

main.py:

class FlaskClass:
    FLAG = True

test.py:

import importlib

path = 'setting.FlaskClass'
p, c = path.rsplit('.', maxsplit=1)
# print(p, c)  # setting FlaskClass
m = importlib.import_module(p)
# print(m)  # <module 'setting' from 'C:\\Users\\Thanlon\\PycharmProjects\\flask_pro\\setting.py'>
cls = getattr(m, c)
print(cls)  # 找到类:<class 'setting.FlaskClass'>
# print(dir(cls))  # 类成员
for key in dir(cls):
    if key.isupper():
        print('key:', key)
        print('value:', getattr(cls, key))
        
"""
<class 'setting.FlaskClass'>
key: FLAG
value: True
"""
2. 配置文件的使用

Flask配置文件是一个 flask.config.Config 对象,它继承字典,默认配置文件如下:

print(type(app.config), app.config)
"""
<class 'flask.config.Config'>
"""
{
   
'ENV': 'production', 
'DEBUG': False, 
'TESTING': False, 
'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None, 
'SECRET_KEY': None, 
'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31),
'USE_X_SENDFILE': False,
'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 
'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_DOMAIN': None, 
'SESSION_COOKIE_PATH': None, 
'SESSION_COOKIE_HTTPONLY': True, 
'SESSION_COOKIE_SECURE': False, 
'SESSION_COOKIE_SAMESITE': None, 
'SESSION_REFRESH_EACH_REQUEST': True, 
'MAX_CONTENT_LENGTH': None, 
'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(seconds=43200), 'TRAP_BAD_REQUEST_ERRORS': None, 
'TRAP_HTTP_EXCEPTIONS': False, 
'EXPLAIN_TEMPLATE_LOADING': False, 
'PREFERRED_URL_SCHEME': 'http', 
'JSON_AS_ASCII': True, 
'JSON_SORT_KEYS': True, 
'JSONIFY_PRETTYPRINT_REGULAR': False, 
'JSONIFY_MIMETYPE': 'application/json', 
'TEMPLATES_AUTO_RELOAD': None, 
'MAX_COOKIE_SIZE': 4093}

修改默认配置文件,创建配置文件 settings.py,用于存放修改的配置文件:

settings.py

# 存放开发环境、线上环境、测试环境共有的配置文件
class Config(object):
    pass
    
# 线上环境配置文件
class ProductionConfig(Config):
    DEBUG = False
    
# 开发环境配置文件
class DevelopmentConfig(Config):
    DEBUG = True
    
# 测试环境配置文件
class TestingConfig(Config):
    DEBUG = True

app.py:

from flask import Flask

app = Flask(__name__)
# print(type(app.config), app.config)
app.config.from_object('settings.DevelopmentConfig')  # 引入配置文件

if __name__ == '__main__':
    app.run()
3. 路由系统
1. 反向生成URL

使用 url_for 方法生成 url

@app.route('/index', methods=['GET', 'POST'], endpoint='name')
def index():
    print(url_for('name'))
    return 'Flask!'

使用 url_for 方法生成url时设置参数:

@app.route('/index/<int:id>')
def index(id):
    print(url_for('index', id=1))
    return 'Flask!'
"""
/index/1
"""

如果不指定 endpoint 的值,默认为函数名:

def _endpoint_from_view_func(view_func):
    assert view_func is not None, 'expected view func if endpoint ' \
                                  'is not provided.'
    return view_func.__name__ # 返回函数名
def add_url_rule(self,rule,endpoint=None,view_func=None,provide_automatic_options=None, **options):
	if endpoint is None:
		endpoint = _endpoint_from_view_func(view_func)
    options['endpoint'] = endpoint
    methods = options.pop('methods', None)
@app.route('/index', methods=['GET', 'POST'])
def index():
    print(url_for('index'))
    return 'Flask!'

如果endpoint非要重名,必须保证函数相同。

2. 动态路由的构造
@app.route('/index/<username>') # 没有指定类型是字符串类型
@app.route('/index/<int:id>')
@app.route('/index/<float:id>') 
@app.route('/index/<path:path>') 

示例:

@app.route('/index/<float:id>') 
def index(id):
    print(type(id),id)
    return 'Flask!'
浏览器输入:http://127.0.0.1:5000/index/1.0
控制台输出:<class 'float'> 1.0
4. 请求与响应相关数据
1. 请求相关数据

常用请求的相关信息:

request.method
request.args
request.form
request.cookies
request.headers

其它请求的相关信息:

request.values
request.path
request.full_path
request.script_root
request.url
request.base_url
request.url_root
request.host_url
request.host
request.files
obj = request.files['the_file_name']
obj.save('/uploads/'+secure_filename(f.filename))
2. 响应响应体

响应字符串:

@app.route('/index')
def index():
    return 'Flask'  # 字符串

响应模板:

@app.route('/index')
def index():
	return render_template() # 模板

响应重定向:

@app.route('/index')
def index():
	return redirect('') # 重定向

响应json格式数据 [from flask import json]:

@app.route('/index')
def index():
    return json.dumps({
   'name': 'thanlon'})  # {'name': 'thanlon'}

另一种响应json格式数据的方式 [from flask import jsonify]:

@app.route('/index')
def index():
    return jsonify({
   'name': 'thanlon'}) # {'name': 'thanlon'}

不能响应字典,即return dic是错误的!

3. 响应响应头

以上是响应响应体,还可以响应响应头和cookie,只需要从Flask模块中导入make_response进行封装即可。(from flask import make_response

示例:将字符串类型的响应体与响应头和cookie一同封装响应:

@app.route('/index')
def index():
    obj = make_response('Flask')
    obj.headers['response_flag'] = 'response_info'
    obj.set_cookie('key','value')
    return obj

响应模板、响应重定向、响应json格式数据和示例就一样的。

5. 简单登录案例
1. 案例目录结构

在这里插入图片描述

2. 案例主要代码

app.py:

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

# app = Flask(__name__, static_folder='static', static_url_path='/thanlon')  # 可指定前缀
app = Flask(__name__)
app.secret_key = 'thanlon'


@app.route('/login', methods=['GET', 'POST'])  # 默认允许GET请求
def login():
    if request.method == 'GET':
        return render_template('login.html')
    username = request.form.get('username')
    pwd = request.form.get('pwd')
    if username == 'Thanlon' and pwd == '123':
        session['username'] = username  # 默认session保存在签名或加密的cookie中
        return redirect('/index')
    # return render_template('login.html', error='用户名或者密码错误!')
    return render_template('login.html', **{
   'error': '用户名或者密码错误!'})


@app.route('/index')  # 默认允许GET请求
def index():
    username = session.get('username')
    if not username:
        return redirect('/login')
    return render_template('index.html')


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

login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>登录</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div class="container">
    <div class="row" style="margin-top:30px">
        <div class="col-md-4 col-md-offset-4">
            <div class="panel panel-primary">
                <div class="panel-heading">登录</div>
                <div class="panel-body">
                    <form method="POST">
                        <div class="form-group">
                            <label>用户名</label>
                            <input type="text" name="username" class="form-control" id="" placeholder="Username"
                                   required="required">
                        </div>
                        <div class="form-group">
                            <label>密码</label>
                            <input type="password" name="pwd" class="form-control" placeholder="Password"
                                   required="required">
                            <div style="color:red">{
  {error}}</div>
                        </div>
                        <button type="submit" class="btn btn-primary">登录</button>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"
        integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
        crossorigin="anonymous"></script>
</html>

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>首页</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div class="container">
    <div class="row" style="margin-top:30px">
        <div class="panel panel-default">
            <div class="panel-body">
                欢迎{
  {session['username']}}进入首页!
            </div>
        </div>
    </div>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"
        integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
        crossorigin="anonymous"></script>
</html>
3. 案例页面效果

登录失败:
在这里插入图片描述
登录成功:
在这里插入图片描述

6. 用户认证
1. 第一种用户认证

settings.py

# 存放开发环境、线上环境、测试环境共有的配置文件
class Config(object):
    DEBUG = False
    SECRET_KEY = 'thanlon'

app.py

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

app = Flask(__name__)
app.config.from_object('settings.DevelopmentConfig')  # 引入配置文件

USER_DICT = {
   
    1: {
   'username': 'thanlon', 'age': '23', 'gender': '男'},
    2: {
   'username': 'kiku', 'age': '25', 'gender': '女'}
}
@app.route('/login', methods=['GET', 'POST'])  # 默认允许GET请求
def login():
    if request.method == 'GET':
        return render_template('login.html')
    username = request.form.get('username')
    pwd = request.form.get('pwd')
    if username == 'Thanlon' and pwd == '123':
        session['username'] = username  # 默认session保存在签名或加密的cookie中
        return redirect('/index')
    # return render_template('login.html', error='用户名或者密码错误!')
    return render_template('login.html', **{
   'error': '用户名或者密码错误!'})

@app.route('/index')
def hello_world():
    if not session.get('username'):
        return redirect(url_for('login'))
    return render_template('index.html', user_dict=USER_DICT)

@app.route('/delete/<int:nid>')
def delete(nid):
    if not session.get('username'):
        return redirect(url_for('login'))
    del USER_DICT[nid]
    return redirect(url_for('hello_world'))  # url_for('')写函数名,和路由无关

@app.route('/detail/<int:nid>')
def detail(nid):
    if not session.get('username'):
        return redirect(url_for('login'))
    info = USER_DICT[nid]
    return render_template('detail.html', info=info)

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

index.html:

……
<div class="container">
    <div class="row">
        <div class="col-md-4">
            <table class="table table-hover">
                <tr>
                    <th style="text-align: center">ID</th>
                    <th style="text-align: center">用户名</th>
                    <th style="text-align: center">年龄</th>
                    <th style="text-align: center">性别</th>
                    <th style="text-align: center">操作</th>
                </tr>
                {% for k,v in user_dict.items() %}
                    <tr style="text-align: center">
                        <td>{
  { k }}</td>
                        <td>{
  { v.username }}</td>
                        <td>{
  { v.age }}</td>
                        <td>{
  { v.gender }}</td>
                        <td><a href="/detail/{
    { k }}">详情</a> | <a href="/delete/{
    { k }}">删除</a></td>
                    </tr>
                    {#                    <tr>#}
                    {#                        <td>{
  { v['username'] }}</td>#}
                    {#                        <td>{
  { v['age'] }}</td>#}
                    {#                        <td>{
  { v['gender'] }}</td>#}
                    {#                    </tr> #}
                    {#                                        <tr>#}
                    {#                                            <td>{
  { v.get('usename','默认') }}</td>#}
                    {#                                            <td>{
  { v.get('age') }}</td>#}
                    {#                                            <td>{
  { v.get('gender') }}</td>#}
                    {#                                        </tr>#}
                {% endfor %}
            </table>
        </div>
    </div>
</div>
……

detail.html

……
<body>
{#{'username': 'thanlon', 'age': '23', 'gender': '男'}#}
<br>
{% for item in info.values() %}{#% for item in info.values() %#}
    {
  { item }}
{% endfor %}
</body>
2. 装饰器知识点补充

函数没有加装饰器:

# coding:utf-8
def auth(func):
    def inner(*args, **kwargs):
        ret = func(*args, **kwargs)
        return ret
    return inner
def index():
    print('index')
print(index.__name__)  # 打印函数名
'''index'''

函数加装饰器,让inner函数伪装成index函数:

# coding:utf-8
def auth(func):
    def inner(*args, **kwargs):
        ret = func(*args, **kwargs)
        return ret
    return inner
@auth
def index():
    print('index')
@auth
def login():
    print('login')
print(index.__name__
  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值