flask01

WSGI(web服务器网关接口)是Python中用来规定web服务器如何与Python web程序进行沟通的标准,本质上就是一个socket服务端。而Werkzeug模块就是WSGI的一个具体实现。

为什么要有flask?

'''
flask性能上基本满足一般web开发的需求,并且灵活性以及可扩展性上要由于其他web框架,对各种数据库的契合度都非常高
关键词:1.性能基本满足要求 2.灵活性可拓展性强 3.对各种数据库的契合度都比较高;
4.在真是的生产环境下,小项目开发快,大项目设计灵活;
'''

flask的安装

pip install flask

安装flask被安装的5个依赖包

包名及版本 功能
Jinjia2-2.10 渲染模板引擎工具集
Markupsafe-1.1.0 可以识别HTML转移规则。HTML字符转移工具集
Werkzeug-0.14.1 Web框架底层库,提供了请求及形影以及开发服务器的功能,简称WSGI工具集
click-7.0 命令行工具集
itsdangerous 加密工具集

flask的四剑客

from flask import Flask ,render_template,redirect,jsonify

app = Flask(__name__)


@app.route('/')
def index():
    #  1.直接返回字符串
    return "返回到前端页面!"

    # 2.返回html
    return render_template('index.html',a='函数render_template传过来的变量!',b=123,c='张超') # 可以接受任意多个变量传到模板上

    # 3.跳转页面
    return redirect('/login')

    # 4.返回json数据
    dic=[{'name':'zhang'},{'name':'李四'}]
    return jsonify(dic)

@app.route('/login')
def login():
    return '你已经跳转到login页面了!'

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

配置文件书写的四种方式

from flask import Flask
app = Flask(__name__)

# 配置文件1,这种方式只能配置如下两种参数
app.debug = True
app.secret_key='123123'

# 配置文件2,以字典的方式
app.config['DEBUG'] = True


# 方式3,配置写入一个单独文件,从文件中导入配置
app.config.from_pyfile('settings1.py')

# 方式4,以文件的形式
app.config.from_object("pro_flask.settings2.TestingConfig")

@app.route('/login')
def login():
    print(123)
    return  "开始登陆。。。。。"

if __name__ == '__main__':
     app.run()
# settings1.py
DEBUG = True
# settings2.py

# 这是基类,所有公用配置单独写的一个类,其他类有需要可以继承此类;
class Config(object):
    DEBUG = False
    TESTING = False
    DATABASE_URL = 'sqlite://:memory:'

# 线上环境配置
class ProductionConfig(Config):
    DATABASE_URL = 'mysql://user@localhost/foo'

# 开发环境配置
class DevelopmentConfig(Config):
    DEBUG = True

# 测试环境配置
class TestingConfig(Config):
    TESTING = True

其他配置方式

# 通过py文件配置
app.config.from_pyfile("python文件名称")
如:
settings.py
DEBUG = True

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

app.config.from_json('json文件名称')
JSON文件名称,必须是json格式,因为内部会执行json.loads

app.config.from_mapping({'DEBUG':True})
字典格式

app.config.from_object('python类或者类的路径')

app.config.from_object('pro_flask.settings.TestingConfig')

settings.py
# 看上例


PS:从sys.path中已经存在路径开始写
PS:settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_pth目录(Flask对象init方法的参数)

路由的本质

endpoint:就相当于给视图函数取了一个别名,通过url_for("别名")可以拿到真正的视图函数的名字;取别名的时候不能重复。
methods:规定了请求方式必须是methods指定的请求方式,请求方式的书写,大小写都可以,源码会把其转为大写;
url_for("函数名或别名"):反向解析;给出一个endpoint指定的函数名或者是别名,能解析出这个函数的路由;

1.1fbv路由示例

from flask import Flask,url_for
app = Flask(__name__)
app.debug = True

#方式一:
# @app.route("/login",,methods=['get',"post"],endpoint='aaa') # 内部除了路由(rule)之外的其他参数都被route方法的**options接收了
# @app.decorator   ===>login=app.decorator(login)
def login():
    return "is login"

# 方式二:
app.add_url_rule('/login',endpoint='aaa',view_func=login,methods=['GET',"POST"])  #  @app.route内部其实就是调用add_url_rule方法,如果我们把这个方法需要的参数都传上,就可以取代装饰器@app.route("/login")


@app.route('/')
def index():
    real_url = url_for('aaa')  # url_for可以获取视图函数的对应请求的路由,参数是别名或函数名
    return real_url


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

1.2路由的源码分析

@app.route("/login") 是一个装饰器,首先执行的是route("/login")函数(函数加括号的优先级最高),返回的是decorator,所以语法糖就变成了@app.decorator,之后执行decorator,在内部又调用了add_url_rule函数

    def route(self, rule, **options):  # rule就是route括号传的路由
        def decorator(f):   # f就是login
            endpoint = options.pop("endpoint", None)  # 此时endpoint为none
            self.add_url_rule(rule, endpoint, f, **options)  # self就是app
            return f
        return decorator
 @setupmethod
    def add_url_rule(
        self,  # app
        rule,  # "/login"
        endpoint=None,  # 接收我们起的别名
        view_func=None, # 接收传来的函数名
        provide_automatic_options=None,
        **options
    ):
       
        if endpoint is None:
            endpoint = _endpoint_from_view_func(view_func)  # endpoint就是函数名login
        options["endpoint"] = endpoint
        methods = options.pop("methods", None)
        if methods is None:
            methods = getattr(view_func, "methods", None) or ("GET",)  # methods没指定请求方式,默认设置成GET
        if isinstance(methods, string_types):
            raise TypeError(
                "Allowed methods have to be iterables of strings, "
                'for example: @app.route(..., methods=["POST"])'
            )
        methods = set(item.upper() for item in methods)
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__

路由携带参数

<int:aid>:路由携带的参数,类似django中的有名分组,可以指定携带参数的数据类型。
# @app.route("/login/<int:aid>",methods=['get',"post"],endpoint='aaa')
# @app.decorator   ===>login=app.decorator(login)
def login(aid):  # aid获取请求携带的参数
    print(type(aid),aid)  # <class 'int'> 100
    return "is login"

app.add_url_rule('/login/<int:aid>',endpoint='aaa',view_func=login,methods=['get',"post"])
用postman验证

# get请求:http://127.0.0.1:5000/login/100
# post请求:http://127.0.0.1:5000/login/100

1.3 默认转换器

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

2.1 CBV 路由示例

from flask import Flask,url_for,views
app = Flask(__name__)
app.debug = True


class IndexView(views.View):
    methods = ['GET','POST']
    # decorators = []
    def dispatch_request(self):
        print("IndexView")
        return 'IndexView!'
    

'''
IndexView.as_view(name='index')走到内部源码,返回的是view,
所以view_func=view,view就是我们指定的name='index',view_func=index
as_views(name='index')为什么要指定name?
如果不指定,就都是view,所以必须指定。

'''

app.add_url_rule('/index',view_func=IndexView.as_view(name='index'),endpoint="123")

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

2.2 源码分析

@classmethod
def as_view(cls, name, *class_args, **class_kwargs):

    def view(*args, **kwargs):  
        self = view.view_class(*class_args, **class_kwargs)  # view.view_class就是我们定义的类,这里就是类加括号实例化对象。
        return self.dispatch_request(*args, **kwargs)  # 分发请求的函数,先从我们类内部定义的方法开始找dispatch_request,

    # 给我们的name='index'添加装饰器,直接在我们的类内部给decorators添加函数属性就行了
    if cls.decorators:
        view.__name__ = name
        view.__module__ = cls.__module__
        for decorator in cls.decorators:
            view = decorator(view)  # decorator就是我们定义的装饰器函数 view就是被装饰的函数,在执行dispatch_request之前如果有一些操作的话,可以这样做。

            # view.view_class = IndexView
            view.view_class = cls
            view.__name__ = name
            view.__doc__ = cls.__doc__
            view.__module__ = cls.__module__
            view.methods = cls.methods   # 我们视图类内部定义的methods允许哪些请求
            view.provide_automatic_options = cls.provide_automatic_options
            return view
#dispatch_request

    def dispatch_request(self):  # 注释告诉我们自定义的类(view的子类)必须要重写dispatch_request方法。
        """Subclasses have to override this method to implement the
        actual view function code.  This method is called with all
        the arguments from the URL rule.
        """
        raise NotImplementedError()

2.3把其写成类似Django类视图函数

from flask import Flask,url_for,views
app = Flask(__name__)
app.debug = True
class IndexView(views.MethodView,):# MethodView类又继承了views.View类,所以走得还是起内部的as_views方法
    methods = ['GET','POST']

    def get(self):
        print('IndexView中get方法!')
        return "get方法!"

    def post(self):
        print('IndexView中post方法!')
        return "post方法!"
    
app.add_url_rule('/index',view_func=IndexView.as_view(name='index'),endpoint="123")

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

在源码走到as_views--->view--->内部调用self.dispatch_request()方法,查找顺序是IndexView--->MethodView(继承了views.View)--->View,在MethodView类内部也写了dispatch_request方法,其源码如下:

class MethodView(with_metaclass(MethodViewType, View)):

    def dispatch_request(self, *args, **kwargs):
        # 请求函数的函数地址
        meth = getattr(self, request.method.lower(), None)  # 获取我们类内部写的请求函数
        if meth is None and request.method == "HEAD":
            meth = getattr(self, "get", None)

        assert meth is not None, "Unimplemented method %r" % request.method
        # 请求函数的函数地址加括号被调用
        return meth(*args, **kwargs)

小案例

Flask中的request请求包,是需要从flask中导入的,不像Django直接携带到请求函数,从形参中直接拿出使用。
from flask import Flask,render_template,request,url_for,redirect

app = Flask(__name__)

USERS = {
    1:{'name':'张三','age':18,'gender':'男','text':'我叫张三'},
    2:{'name':'李四','age':28,'gender':'男','text':'我叫李四'},
    3:{'name':'王五','age':38,'gender':'女','text':'我叫王五'},
}

@app.route('/detail/<int:nid>',methods=['GET'])
def detail(nid):
    print("nid:",nid)
    info = USERS.get(nid)
    return render_template('detail.html',info=info,nid=nid)

@app.route('/index',methods=['GET'])
def index():
    return render_template('index1.html',user_dict = USERS)

def login():
    if request.method == "GET":
        return render_template('login.html')

    else:
        name = request.form.get('name')
        pwd = request.form.get('pwd')
        if name == "zhang" and pwd=='123':
            url = url_for('index')  #  给一个路由对应的函数名或别名,能反向解析出其路由
            print(url)  # /index
            return redirect(url)    # 重定向到该路由
            # session['user_info'] = user
        return render_template('login.html',error='用户名或密码错误')

app.add_url_rule('/login',view_func=login,methods=['GET','POST'])
if __name__ == '__main__':

    app.run()
# detail.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
user_dict
<h1>单个用户列表</h1>
<table>
    <tr>
        <th>编号</th>
        <th>姓名</th>
        <th>年龄</th>
        <th>性别</th>
        <th>详情</th>
    </tr>
    <tr>
        <td>{{ nid }}</td>
        <td>{{ info.name}}</td>
        <td>{{ info['age'] }}</td>
        <td>{{ info.get('gender') }}</td>
        <td>{{ info.text }}</td>
    </tr>

</table>
</body>
</html>
# index1.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
user_dict
<h1>用户列表</h1>
<table>
    {% for k,v in user_dict.items() %}  <!-- flask中模板渲染items需要加括号,Django就不能加 -->
    <tr>
        <th>编号</th>
        <th>姓名</th>
        <th>年龄</th>
        <th>性别</th>
        <th>详情</th>
    </tr>
    <tr>
        <td>{{ k }}</td>
        <td>{{ v.name}}</td>
        <td>{{ v['age'] }}</td>
        <td>{{ v.get('gender') }}</td>
        <td>{{ v.text }}</td>
        <td><a href="/detail/{{k}}">查看详情</a></td>
    </tr>
    {% endfor %}
</table>
</body>
</html>

<!-- flask的模板渲染支持三种取值方式,而Django只支持点取值 -->
# login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录界面</title>
</head>
<body>
<form action="" method="POST">
    用户:<input type="text" name="name">
    密码:<input type="password" name="pwd">
    <input type="submit" value="提交">&nbsp;&nbsp;{{ error }}  <!-- ajax提交,不能设置submit-->
</form>
</body>
</html>

猜你喜欢

转载自www.cnblogs.com/zhangchaocoming/p/12345572.html