Flask 基础

学习Flask的一些基础知识总结

Flask是个轻量级的web框架,可扩展性强,有大量第三方组件,最终可以实现类似Django这种大而全的重量级框架的功能。区别比较大的一点是;

在Django中request是在内部不断的进行封装和传递,而在Flask中则是基于上下文管理,把它放在一个地方,当需要用时去那里取即可。

Flask是基于werkzug实现wsgi协议的:

当我们不使用flask而仅仅使用werkzug来实现响应请求:

from werkzeug.serving import run_simple

def func(environ, start_response):
    print('请求来了')


if __name__ == '__main__':
    run_simple('127.0.0.1', 5000, func) 

当访问127.0.0.1:5000时即可看到console打印出的请求来了,当然因为没有返回正常的response,而报错。

配置:

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

# 方式一:

    def from_envvar(self, variable_name, silent=False):
        rv = os.environ.get(variable_name)
        if not rv:
            if silent:
                return False
            raise RuntimeError(
                "The environment variable %r is not set "
                "and as such configuration could not be "
                "loaded.  Set this variable and make it "
                "point to a configuration file" % variable_name
            )
        return self.from_pyfile(rv, silent=silent)

从环境变量中获取,事实上这个变量是一个配置文件,它最后是调用from_pyfile去文件中获取变量。 

#方式二

    def from_pyfile(self, filename, silent=False):
        filename = os.path.join(self.root_path, filename)
        d = types.ModuleType("config")
        d.__file__ = filename
        try:
            with open(filename, mode="rb") as config_file:
                exec(compile(config_file.read(), filename, "exec"), d.__dict__)
        except IOError as e:
            if silent and e.errno in (errno.ENOENT, errno.EISDIR, errno.ENOTDIR):
                return False
            e.strerror = "Unable to load configuration file (%s)" % e.strerror
            raise
        self.from_object(d)
        return True

从一个py文件中获取,最后调用的是from_object方法 。

方式三:

  #app.from_objects()
  def from_object(self, obj):
        if isinstance(obj, string_types):
            obj = import_string(obj)
        for key in dir(obj):
            if key.isupper():
                self[key] = getattr(obj, key)

  object可以是字符串,通过import_string导入。也可以是一个对象,直接获取对象的属性。

#方式四   

def from_json(self, filename, silent=False):
        filename = os.path.join(self.root_path, filename)

        try:
            with open(filename) as json_file:
                obj = json.loads(json_file.read())
        except IOError as e:
            if silent and e.errno in (errno.ENOENT, errno.EISDIR):
                return False
            e.strerror = "Unable to load configuration file (%s)" % e.strerror
            raise
        return self.from_mapping(obj)

从json文件中获取,json文件中本质是一个字典,所以最后调用方式五from_mapping

#方式五:

    def from_mapping(self, *mapping, **kwargs):
        mappings = []
        if len(mapping) == 1:
            if hasattr(mapping[0], "items"):
                mappings.append(mapping[0].items())
            else:
                mappings.append(mapping[0])
        elif len(mapping) > 1:
            raise TypeError(
                "expected at most 1 positional argument, got %d" % len(mapping)
            )
        mappings.append(kwargs.items())
        for mapping in mappings:
            for (key, value) in mapping:
                if key.isupper():
                    self[key] = value
        return True

从键值对中获取配置,不是大写的配置将被忽略掉。

 

常用的两种方式:

基于文件的方式:

在本地设置localsettings, prodsettings.py, 在prodsettings中加载localsettings中的内容,如果没有就什么也不做
 

try:
    from .localsettings import *
except ImportError:
    pass 

基于类的方式,即From_object

class BaseSettings(object):
    x = '1'

class LocalSettings(BaseSettings):
    Host = 'localhost'

class ProdSettings(BaseSettings):
    Host = '127.0.0.1' 
app.config.from_object('config.settings.LocalSettings') 

 

Flask路由:

路由加载流程

将url,函数打包成rule对象
将rule对象添加到map对象中
map对象封装在app.url_map = map对象

方式一:

def index():
    return render_template('index.html')
app.add_url_rule('/index', 'index', index)

最后面的index取了视图函数的名称 

方式二:推荐的方式

@app.route('/login',methods=['GET','POST'],endpoint="login")
def login():
    pass

这里的“/login'即是路由的写法,且前面的/不能省略, methods则定义了允许的请求方法, endpoint是别名,类似于django中的name.与django中相同的是,endpoint也就是别名,不能存在两个相同的别名,否则不知道怎么分发路由而报错。

动态路由:

@app.route('/index/<name>')
def login(name):
    pass

@app.route('/index/<int:nid>')
def login(nid):
    pass

看第一个login , <name>即是传递的关键字参数,默认情况下这个参数是字符串类型,第二个视图中指定了int类型,这样我们获取到的参数就是Int类型,那么我们如何获取参数呢?

获取提交的数据:

from flask import request

@app.route('/index')
def login():
    request.args # GET形式传递的参数
    request.form # POST形式提交的参数

get携带的参数保存在request.args中,而表单提交的数据则保存在request.form中

返回数据:

from flask import render_template, jsonify,redirect
@app.route('/index')
def login():
    return render_template('模板文件')
    return jsonify()
    reutrn redirect('/index/') # reutrn redirect(url_for('idx'))

render_template相当于django中的render, jsonify相当于django中的JsonResponse, redirect则与django相同,重定向。

redirect的参数可以是路由,也可以是别名,但如果写别名,则需要写成url_for()这样。

 

视图:

FBV

def index():
    return render_template('index.html')
app.add_url_rule('/index', 'index', index)

# 公司里一般用这种方式
@app.route('/login')
def login():
    return render_template('login.html')  

CBV

def test1(func):
    def inner(*args, **kwargs):
        print('before1')
        result = func(*args, **kwargs)
        print('after1')
        return result
    return inner

def test2(func):
    def inner(*args, **kwargs):
        print('before2')
        result = func(*args, **kwargs)
        print('after2')
        return result
    return inner 

class UserView(views.MethodView):
    methods = ['GET',"POST"]

    decorators = [test1,test2]

    def get(self):
        print('get')
        return 'get'

    def post(self):
        print('post')
        return 'post'

app.add_url_rule('/user', view_func=UserView.as_view('user')) # endpoint 

CBV只能用add_url_rule的方式注册路由,其中as_view中的参数user即是endpoint

视图返回数据了在前端要怎么渲染呢?

模板渲染:

flask中默认使用jinja2浸染引擎,它比django默认的引擎更灵活,在模板中Jinjia2支持block, include,extends等django中支持的语法,但也有些不同:

变量 {{name}}与django相同

  {% for key, value in data_dict.items() %}
        <tr>
            <td>{{ key }}</td>
            <td>{{ value.name }}</td>
            <td>{{ value.age }}</td>
            <td>
                <a href="/edit?id={{ key }}">编辑</a>
                <a href="/delete?id={{ key }}">删除</a>
            </td>
        </tr>
    {% endfor %}

与Django的区别:

字典方法需要加括号:data_dict.itmes()

字典中的值有有三种方式:value.name, value['name'], value.get('name')

定义全局模板方法:

@app.template_global() #  {{ func("海宇") }}
def func(arg):
    return '海子' + arg  

这样在每个视图中都可以调用,注意它需要带括号

{% block content %}
    <h1>MD</h1>
    {% include 'form.html' %}
    {{ func("汪洋") }}
{% endblock %} 
@app.template_filter() # {{ "海宇"|x1("宇") }}
def x1(arg,name):
    return '狗子' + arg + name  

注意filter是在|在后面

app装饰的全局可以用,在蓝图中注册只能蓝图中注册的视图函数可用

特殊的装饰器:

@app.before_request
def f1():
    if request.path == '/login':
        return
    print('f1')

@app.after_request
def f10(response):
    print('f10')
    return response 

看看它的执行顺序:
from flask import Flask,render_template,request

app = Flask(__name__)

@app.before_request
def f1():
    print('f1')

@app.before_request
def f2():
    print('f2')

@app.after_request
def f10(response):
    print('f10')
    return response

@app.after_request
def f20(response):
    print('f20')
    return response

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

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

注:这里的装饰器可以这样写:

def x1():
    print('xxx')
app.before_request(x1) 

输出结果:
f1
f2
index
127.0.0.1 - - [21/Nov/2019 16:16:25] "GET /index HTTP/1.1" 200 -
f20
f10
它的顺序是在before_request按加载顺序,而after_request则将加载顺序反转。

上一篇:Django 获取model的verbose_name

下一篇:Flask 路由的对应关系之mapping is overwriting


评论 共1人参与1条评论)
andy 发表于 2019-11-24 09:55:58
一级评论测试