Flask详解(上篇)

一 什么是Flask

Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,
其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器

与Django web框架对比
Django:

  • 优点: 大而全,里面组件非常的多
  • 缺点: 资源浪费严重

Flask:
优点:

  • 短小精悍
  • 官方承认支持第三方组件

缺点:

  • 稳定性相对其他web框架较差

二  Werkzeug WSGI

# wsgi 应用程序网关接口 把请求处理后发送给对应的app中

wsgi:流程图:

from werkzeug.wrappers import Request, Response

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


if __name__ == '__main__':
    from werkzeug.serving import run_simple

    run_simple('localhost', 4000, hello)

三 Flask基本使用(实现一个最基本的Hello World)

from flask import Flask

app = Flask(__name__)


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


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

四 路由系统

使用装饰器的形式添加路由

@app.route('/user/<username>')
@app.route('/post/<int:post_id>')
@app.route('/post/<float:post_id>')
@app.route('/post/<path:path>')
@app.route('/login', methods=['GET', 'POST'])
# 注意app.route 本质上是执行的app.add_url_rule方法
- endpoint,反向生成url
    @app.route('/index/', endpoint='index')
- url_for("index")

常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理:

DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter,
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}

自定制正则路由匹配

from flask import Flask, views, url_for
from werkzeug.routing import BaseConverter

app = Flask(import_name=__name__)


class RegexConverter(BaseConverter):
    """
    自定义URL匹配正则表达式 
    """
    def __init__(self, map, regex):
        super(RegexConverter, self).__init__(map)
        self.regex = regex

    def to_python(self, value):
        """
        路由匹配时,匹配成功后传递给视图函数中参数的值
        :param value: 
        :return: 
        """
        return int(value)

    def to_url(self, value):
        """
        使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
        :param value: 
        :return: 
        """
        val = super(RegexConverter, self).to_url(value)
        return val


# 添加到flask中
app.url_map.converters['regex'] = RegexConverter


@app.route('/index/<regex("d+"):nid>')
def index(nid):
    print(url_for('index', nid='888'))
    return 'Index'


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

@app.route和app.add_url_rule参数:

rule,                       URL规则
view_func,                  视图函数名称
defaults=None,              默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数
endpoint=None,              名称,用于反向生成URL,即: url_for('名称')
methods=None,               允许的请求方式,如:["GET","POST"]


strict_slashes=None,        对URL最后的 / 符号是否严格要求,
                            如:
                                @app.route('/index',strict_slashes=False),
                                    访问 http://www.xx.com/index/ 或 http://www.xx.com/index均可
                                @app.route('/index',strict_slashes=True)
                                    仅访问 http://www.xx.com/index 
redirect_to=None,           重定向到指定地址
                            如:
                                @app.route('/index/<int:nid>', redirect_to='/home/<nid>')
                                或
                                def func(adapter, nid):
                                    return "/home/888"
                                @app.route('/index/<int:nid>', redirect_to=func)
subdomain=None,             子域名访问
                                    from flask import Flask, views, url_for

                                    app = Flask(import_name=__name__)
                                    app.config['SERVER_NAME'] = 'wupeiqi.com:5000'


                                    @app.route("/", subdomain="admin")
                                    def static_index():
                                        """Flask supports static subdomains
                                        This is available at static.your-domain.tld"""
                                        return "static.your-domain.tld"


                                    @app.route("/dynamic", subdomain="<username>")
                                    def username_index(username):
                                        """Dynamic subdomains are also supported
                                        Try going to user1.your-domain.tld/dynamic"""
                                        return username + ".your-domain.tld"


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

五 视图函数

1 FBV

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

# 生成路由关系,并把关系保存到某个地方,app对象的 url_map字段中
@app.route('/xxxx')  # @decorator
def index():
    return "Index"



if __name__ == '__main__':
    # 启动程序,监听用户请求
    # 一旦请求到来,执行 app.__call__方法
    # 封装用户请求
    # 进行路由匹配

2 CBV

from flask import Flask,views
class IndexView(views.MethodView):
    methods = ['GET']
    decorators = [auth, ]

    def dispatch_request(self):
        print('Index')
        return 'Index!'
    def get(self):
        return "I am GET"
    
    def post(self):
        return "I am POST"
app.add_url_rule('/index', endpoint='class_index', view_func=IndexView.as_view(name='index'))  # name=endpoint

3 Flask的一个登录示例

from flask import Flask, render_template, request, redirect, session
app = Flask(__name__, template_folder="templates", static_folder='static')
app.secret_key = 'asefsdf'

@app.route('/login/', methods=["GET", "POST"])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    user = request.form.get('user')
    pwd = request.form.get('pwd')
    print(user, pwd)
    if user == 'harry' and pwd == 'cs1993413':
        session['user'] = user
        return redirect('/index')
    return render_template('login.html', error='用户名或密码错误')
    # return render_template('login.html', **{error='用户名或密码错误'})

@app.route('/index/')
def index():
    user = session.get('user')
    if not user:
        return redirect('/login')
    return render_template('index.html')

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

六 Flask配置文件

flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:

{
    '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,
}

1. 修改方法一单个修改

利用点来修改,该方法能修改的配置信息数量有限

app.debug = True
app.secret_key = 'asdfgh'

2. 修改方法二——利用字典修改

app.config['DEBUG'] = True
# 由于配置文件继承自字典,所以可以用修改字典的方式修改
# 由于Config对象本质上是字典,所以还可以使用app.config.update(...)

3. 修改方法三——通过py文件修改(常用)

  • 从sys.path中已经存在路径开始写

  • settings.py文件默认路径要放在程序root_path目录。如果没有放在根目录下,instance_relative_config为True,则目录就是instance_path目录(Flask对象init方法的参数)

instance_relative_config = True
instance_path = '配置文件settings.py的目录'
# 利用from_pyfile来修改配置
app.config.from_pyfile(文件名)

1. 创建一个配置文件settings.py
2. 在settings.py中写配置
3. 利用app.from_pyfile('settings.py'),来讲默认配置文件中的配置替换掉
# settings.py
SECRET_KEY = '123asdf'


# main.py
from flask import Flask, session

app = Flask(__name__)
app.config.from_pyfile('settings.py')    # 利用from_pyfile来修改配置

@app.route('/')
def func():
    session['user'] = 'tom'
    return session.get('user')

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

4. 修改方法四——利用类或类的路径修改(常用)

  • 从sys.path中已经存在路径开始写

  • settings.py文件默认路径要放在程序root_path目录。如果没有放在根目录下,instance_relative_config为True,则目录就是instance_path目录(Flask对象init方法的参数)

instance_relative_config = True
instance_path = '配置文件settings.py的目录'
app.config.from_mapping({'DEBUG': True})   # 字典格式
app.config.from_object("python类或类的路径")

# 利用类或类的路径,如下:
# main.py
    app.config.from_object('pro_flask.settings.TestingConfig')
    
# settings.py
    class Config(object):
        DEBUG = False
        TESTING = False
        DATABASE_URI = 'sqlite://:memory:'

    class ProductionConfig(Config):
        DATABASE_URI = 'mysql://user@localhost/foo'

    class DevelopmentConfig(Config):
        DEBUG = True

    class TestingConfig(Config):
        TESTING = True

5. 其他方法修改配置

# 1.通过环境变量配置from_envvar
app.config.from_envvar("环境变量名称")
app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
环境变量的值为python文件名称名称,内部调用from_pyfile方法

# 2.from_json
app.config.from_json("json文件名称")
JSON文件名称,必须是json格式,因为内部会执行json.loads
原文地址:https://www.cnblogs.com/harryblog/p/11128231.html