初识Flask

Flask介绍

flask是一个短小精悍、可扩展性强的一个Web框架。

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

werkzeug

werkzeug是实现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)
werkzeug

Flask简单使用

from flask import Flask

app=Flask(__name__)

@app.route('/index')
def index():
    return 'hello world'

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

配置文件

flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
    {
        'DEBUG':                                get_debug_flag(default=False),  是否开启Debug模式
        'TESTING':                              False,                          是否开启测试模式
        'PROPAGATE_EXCEPTIONS':                 None,                          
        'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
        'SECRET_KEY':                           None,
        'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
        'USE_X_SENDFILE':                       False,
        'LOGGER_NAME':                          None,
        'LOGGER_HANDLER_POLICY':               'always',
        'SERVER_NAME':                          None,
        'APPLICATION_ROOT':                     None,
        'SESSION_COOKIE_NAME':                  'session',
        'SESSION_COOKIE_DOMAIN':                None,
        'SESSION_COOKIE_PATH':                  None,
        'SESSION_COOKIE_HTTPONLY':              True,
        'SESSION_COOKIE_SECURE':                False,
        'SESSION_REFRESH_EACH_REQUEST':         True,
        'MAX_CONTENT_LENGTH':                   None,
        'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
        'TRAP_BAD_REQUEST_ERRORS':              False,
        'TRAP_HTTP_EXCEPTIONS':                 False,
        'EXPLAIN_TEMPLATE_LOADING':             False,
        'PREFERRED_URL_SCHEME':                 'http',
        'JSON_AS_ASCII':                        True,
        'JSON_SORT_KEYS':                       True,
        'JSONIFY_PRETTYPRINT_REGULAR':          True,
        'JSONIFY_MIMETYPE':                     'application/json',
        'TEMPLATES_AUTO_RELOAD':                None,
    }

通过类进行配置

app.config.from_object("settings.DevelopmentConfig")
        
        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
import_name,
        static_url_path=None,   设置静态文件url
        static_folder='static',     默认静态文件路径
        static_host=None,
        host_matching=False,
        subdomain_matching=False,
        template_folder='templates',  默认模板路劲
        instance_path=None,
        instance_relative_config=False,
        root_path=None

路由系统

  • @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参数:
    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()

反向生成url

- endpoint,反向生成URL,默认函数名
@app.route('/index/',methods=['GET'],endpoint='n1')
def hello():
       url=url_for('n1') 

#没有定义endpoint
       url=url_for('hello')

        - 动态路由:
            @app.route('/index/<int:nid>',methods=['GET','POST'])
            def index(nid):
                print(nid)
      #动态路由反向生成:url_for("index",nid=777)

                return "Index"    

路由系统的使用:

FBV

def auth(func):
    def inner(*args, **kwargs):
        print('before')
        result = func(*args, **kwargs)
        print('after')
        return result
    return inner
    

@app.route('/index.html',methods=['GET','POST'],endpoint='index')
@auth
def index():
    return 'Index'def index():
    return "Index"

self.add_url_rule(rule='/index.html', endpoint="index", view_func=index, methods=["GET","POST"])
or
app.add_url_rule(rule='/index.html', endpoint="index", view_func=index, methods=["GET","POST"])
app.view_functions['index'] = index

CBV

def auth(func):
    def inner(*args, **kwargs):
        print('before')
        result = func(*args, **kwargs)
        print('after')
        return result

    return inner

class IndexView(views.View):
    methods = ['GET']
    decorators = [auth, ]

    def dispatch_request(self):
        print('Index')
        return 'Index!'

app.add_url_rule('/index', view_func=IndexView.as_view(name='index'))  # name=endpoint
class IndexView(views.MethodView):
    methods = ['GET']
    decorators = [auth, ]

    def get(self):
        return 'Index.GET'

    def post(self):
        return 'Index.POST'


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

自定义正则

from flask import Flask,url_for

app = Flask(__name__)

# 步骤一:定制类
from werkzeug.routing import BaseConverter
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

# 步骤二:添加到转换器
app.url_map.converters['reg'] = RegexConverter

"""
1. 用户发送请求
2. flask内部进行正则匹配
3. 调用to_python(正则匹配的结果)方法
4. to_python方法的返回值会交给视图函数的参数

"""

# 步骤三:使用自定义正则
@app.route('/index/<reg("d+"):nid>')
def index(nid):
    print(nid,type(nid))

    print(url_for('index',nid=987))
    return "index"

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

请求

   from flask import Flask
    from flask import request
    from flask import render_template
    from flask import redirect
    from flask import make_response

    app = Flask(__name__)


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

        # 请求相关信息
        # request.method    获取请求方式
        # request.args         获取get方法的参数
        # request.form        获取form表单提交的数据
        # request.values
        # request.cookies
        # request.headers
        # 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('/var/www/uploads/' + secure_filename(f.filename))


        return "内容"

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

 响应

        # 响应相关信息
        return "字符串"
        return render_template('html模板路径',**{})
         return redirect('/index')

       #自定制响应头
        response = make_response(render_template('index.html'))
        response是flask.wrappers.Response类型
        response.delete_cookie('key')
        response.set_cookie('key', 'value')
         response.headers['X-Something'] = 'A value'
         return response

模板

基本数据类型

基本数据类型:可以执行python语法,如:

#字典
                {{values.get('name')}}
                {{values['name']}}
                #列表
                {{values[0]}}
                #函数
                {{func(可传参数)}}
                #循环
                {%for item in xxx%}
                {%endfor%}
        传入函数
            - django,自动执行
            - flask,不自动执行

全局定义函数

@app.template_global()
    def sb(a1, a2):
        # {{sb(1,9)}}
        return a1 + a2

@app.template_filter()
    def db(a1, a2, a3):
        # {{ 1|db(2,3) }}
        return a1 + a2 + a3

模板(继承、块、安全、宏定义)

继承和块

- 模板继承
            layout.html
                <!DOCTYPE html>
                <html lang="zh-CN">
                <head>
                    <meta charset="UTF-8">
                    <title>Title</title>
                    <meta name="viewport" content="width=device-width, initial-scale=1">
                </head>
                <body>
                    <h1>模板</h1>
                    {% block content %}{% endblock %}
                </body>
                </html>
            
            tpl.html
                {% extends "layout.html"%}
                {% block content %}
                    {{users.0}}
                {% endblock %}    

include

include 
            {% include "form.html" %}
            form.html 
                <form>
                    asdfasdf
                    asdfasdf
                    asdf
                    asdf
                </form>

宏定义

相当于定义了一个函数,

在全局都可以调用

{% macro ccccc(name, type='text', value='') %}
                <h1>宏</h1>
                <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
                <input type="submit" value="提交">
            {% endmacro %}

            {{ ccccc('n1') }}

            {{ ccccc('n2') }}

安全

安全
            - 前端: {{u|safe}}
            - 后端: MarkUp("<a></a>")

Session

当请求刚到来:flask读取cookie中session对应的值:eyJrMiI6NDU2LCJ1c2VyIjoib2xkYm95,

将该值解密并反序列化成字典,放入内存以便视图函数使用。

视图函数:
    @app.route('/ses')
    def ses():
        session['k1'] = 123
        session['k2'] = 456
        del session['k1']

        return "Session"
            session['xxx'] = 123

当请求结束时,flask会读取内存中字典的值,进行序列化+加密,写入到用户cookie中。

闪现

闪现,在session中存储一个数据,读取时通过pop将数据移除。

from flask import Flask,flash,get_flashed_messages
@app.route('/page1')
def page1():
    flash('临时数据存储','error')
    flash('sdfsdf234234','error')
    flash('adasdfasdf','info')
    return "Session"
@app.route('/page2')
def page2():
    print(get_flashed_messages(category_filter=['error']))
    return "Session"

中间件

call方法什么时候出发?
    - 用户发起请求时,才执行。
任务:在执行call方法之前,做一个操作,call方法执行之后做一个操作。
    class Middleware(object):
        def __init__(self,old):
            self.old = old

        def __call__(self, *args, **kwargs):
            ret = self.old(*args, **kwargs)
            return ret


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

特殊装饰器

@before_request

#在执行视图函数之前,requset都要经过(before_request)

@after_request(response)

必须:return response

#在执行视图函数之后,requset都要经过(after_request)

@after_request/@before_request实例

@app.before_request
def x1():
    print('before:x1')
    return ''

@app.before_request
def xx1():
    print('before:xx1')

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

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

@app.route('/index')
def index():
    print('index')
    return "Index"        

@template_global

模板中有使用方法

@template_filter

 模板中有使用方法

@errorhandler

 出现错误时,自定制返回的内容

@app.errorhandler(404)
def not_found(arg):
  #arg 错误信息
print(arg) return "没找到"

蓝图

目标:给开发者提供目录结构

1.创建与项目名相同的文件夹

2.在该文件下创建__init__.py

from flask import Flask
def create_app():
    app = Flask(__name__)
    return app

3.在与文件同一级的目录下创建manage.py

from crm import create_app
app=create_app()
if __name__ == '__main__':
    app.run()

4.创建该项目的静态文件和模板

5.蓝图的关系的创建

user.py

from flask import Blueprint
us =Blueprint('us',__name__)
@us.route('/login')
def login():
    return 'login'
@us.route('/logout')
def logout():
    return 'logout'

__init__.py

from flask import Flask
from .views.user import us
def create_app():
    app = Flask(__name__)
    app.register_blueprint(us)
    return app

6.蓝图前缀+before方法

可以为每个蓝图加上前缀,比如127.0.0.1/admin/xxx/xxx

127.0.0.1/web/xxx/xxx

加上前缀:

在__init__.py文件里面:

from flask import Flask
from .views.user import us
from .views.account import ac
def create_app():
    app = Flask(__name__)
    app.register_blueprint(us,url_prefix='/admin')
    app.register_blueprint(ac,url_prefix='/web')
    return app
  •  在__init__.py 里面加上@before_request 方法把所有的请求都会经过
  • 在每一个蓝图加上@before_request 那么就意味着只有该蓝驱的所有请求经过

 before_request源码流程图

 

 示例

Session+Flask实现用户登录

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

app=Flask(__name__)

#session 如果需要用到session 需要加盐.
# 本质上他是放在cookies里,并不是像django一样放在数据库
app.secret_key ='213fdasfa123df'

@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')
    if user=='ming' and pwd=='123':
        session['user_info']={'username':user}
        return redirect('/index')

    return render_template('login.html',error='用户名或密码错误')
    # return render_template('login.html',**{'error':'用户名或者密码错误'})


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

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

 学生管理

@app.route('/index')
def index():
    if not session.get('user'):
        return redirect(url_for('login'))
    return render_template('index.html',stu_dic=STUDENT_DICT)
#版本二:
import functools
def auth(func):
    @functools.wraps(func)
    def inner(*args,**kwargs):
        if not session.get('user'):
            return redirect(url_for('login'))
        ret = func(*args,**kwargs)
        return ret
    return inner

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

#应用场景:比较少的函数中需要额外添加功能。
    
#版本三:before_request
@app.before_request
def xxxxxx():
    if request.path == '/login':
        return None

    if session.get('user'):
        return None

    return redirect('/login')
原文地址:https://www.cnblogs.com/chenxuming/p/9414731.html