Flask 基础

Falsk

Flask is a microframework for Python based on Werkzeug, Jinja 2 and good intentions.

“微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。

默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用。

Start Flask

pip insatll flask

werkzeug 简单应用

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) # 开启本地 监听4000端口,绑定函数

Flask 简单应用

1.简单应用
# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask
app = Flask(__name__)

app.debug = True
@app.route("/")
def sayhello():

    return "hello word"

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

2.配置文件系统

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['DEBUG'] = True
 
    PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...)
 
方式二:
    app.config.from_pyfile("python文件名称")
        如:
            settings.py
                DEBUG = True
 
            app.config.from_pyfile("settings.py")
 
    app.config.from_envvar("环境变量名称")
        环境变量的值为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
 
            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
 
        PS: 从sys.path中已经存在路径开始写
     
 
    PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录
3.路由系统
# _*_ coding:utf-8 _*_
# __author__Zj__

# 路由系统 函数 视图
from flask import Flask
app = Flask(__name__)
app.debug = True

# 源码实例 router() 装饰器的原理 
"""
def route(self, rule, **options):
  def decorator(f):
    endpoint = options.pop('endpoint', None)
    self.add_url_rule(rule, endpoint, f, **options)
    return f
return decorator      
"""
@app.route("/index")
def sayhello():
    return "hello word"
def login():
    return "login"
app.add_url_rule("/", "n2", login, methods=["GET", "POST"] )

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'])
常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理:

DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter,
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}
3.1 其次我们也可以自己注册自己的规则
# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask
from werkzeug.routing import BaseConverter
app = Flask(__name__)

# 自定制正则路由匹配

class RegexConverter(BaseConverter):

    def __init__(self, map, regex):
        super(RegexConverter, self).__init__(map)
        self.regex = regex

    def to_python(self, value):
        """
        这里面对匹配到的字符串就行加工
        :param value:
        :return:
        """
        return value

    def to_url(self, value):
        """
        反向生成url的时候需要用到
        :param value:
        :return:
        """
        val = super(RegexConverter, self).to_python(value)
        return val


app.debug = True

app.url_map.converters["regex"] = RegexConverter # 注册自己自己的url路有关系


@app.route("/")
def sayhello():
    return "hello word"


@app.route("/index/<regex('xb\d+'):nid>", methods=["GET"])
def index(nid):
    print(nid)
    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"
4.CBV 模式的分发规则

# _*_ coding:utf-8 _*_
# __author__Zj__

# 类视图的路由

from flask import Flask, views

app = Flask(__name__)
app.debug = True


def auth(func):
    def inner(*args, **kwargs):
        return func(*args, **kwargs)

    return inner


class IndexView(views.MethodView):
    methods = ["GET", ] method 的list
    decorators = [auth, ] # 装饰器的list

    def get(self):
        return "index.get"

    def post(self):
        return "index.get"

# 注册路由规则,加类  name 是 反向的url 名称
app.add_url_rule('/index', view_func=IndexView.as_view(name="index"))

if __name__ == '__main__':
    app.run()
5.模板语言

1、模板的使用
Flask使用的是Jinja2模板,所以其语法和Django无差别。

2、自定义模板方法
Flask中自定义模板方法的方式和Bottle相似,创建一个函数并通过参数的形式传入render_template

# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask, render_template, Markup, jsonify, make_response

app = Flask(__name__)
app.debug = True


def func(n):
    return n + "sssss" # 自定义函数
    
    
def input(s):
    text = "<input type='text' value=%s />" % s # 自定义input标签,用makeup来保证安全性
    text = Markup(text)
    return text

@app.route("/")
def sayhello():
    return render_template("s7.html", **{"func": func, "input": input})
if __name__ == '__main__':
    app.run(port=8001)
注意:Markup等价django的mark_safe
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--支持函数的使用-->
<h2>{{func("www")}}</h2>

{{input("11")}}


{{input("11")|safe}} # 也可以这样 如果后端不做makeup的时候 这样也说明是安全的


<!-- 宏定义 类似函数-->
{% macro input(name, type='text', value='') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}

{{ input('n1') }}

</body>
</html>
5.1自定义模板语言
@app.template_global()
def sb(a1, a2):
    return a1 + a2


@app.template_filter() 
def db(a1, a2, a3):
    return a1 + a2 + a3

使用:
{{sb(1,2)}}  {{ 1|db(2,3)}}
5.2 cookies的设定
# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask, render_template, Markup, jsonify, make_response

app = Flask(__name__)
app.debug = True

@app.route("/", methods=["GET"])
def index():

    # text = "cookies"
    # text = make_response(text)
    # text.set_cookie("XXXX") #设置cookies
    html = render_template("s7.html",text="1111")
    response = make_response(html) 
    response.headers["xxx"] = "xxxx" # 设置headers
    return response
if __name__ == '__main__':
    app.run(port=8001)

6.请求相应相关参数

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
        # request.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存储
        # obj.save('/var/www/uploads/' + secure_filename(f.filename))

        # 响应相关信息
        # return "字符串"
        # return render_template('html模板路径',**{})
        # return redirect('/index.html')
        
        # 对响应进行包装
        
        # 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

        return "string"

    if __name__ == '__main__':
        app.run()
6.1 Session相关

基本使用(lower)

# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask, render_template, request, redirect, session, url_for

app = Flask(__name__)
app.debug = True


# app.config["SECRET_KEY"] = "1234"
app.secret_key = "123" # 开头需要加盐 这样才不会报错 两种形式


USERS = {
    1: {'name': '老王', 'age': 18, 'gender': '男',
        'text': "当眼泪掉下来的时候,是真的累了, 其实人生就是这样: 你有你的烦,我有我的难,人人都有无声的泪,人人都有难言的苦。 忘不了的昨天,忙不完的今天,想不到的明天,走不完的人生,过不完的坎坷,越不过的无奈,听不完的谎言,看不透的人心放不下的牵挂,经历不完的酸甜苦辣,这就是人生,这就是生活。"},
    2: {'name': '老李', 'age': 28, 'gender': '男',
        'text': "高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"},
    3: {'name': '老鸨', 'age': 18, 'gender': '女',
        'text': "高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"},
}

import functools

# 装饰器的使用,每次来验证session 


# 因为有一点需要明确 就是 endpoint 不指定默认 用函数名
# 导致我们加装饰器的时候 每次返回的inners函数的名字不能一样 需要改成 传入的 func函数的名字


def verfriy(func):
    @functools.wraps(func) # 函数属性转移
    def inners(*args, **kwargs):
        user = session.get("user_info")
        if not user:
            url = url_for("login")
            return redirect(url)
        return func(*args, **kwargs)

    # inners.__name__ = func.__name__ # 也可以这样做 

    return inners


@app.route("/", methods=["GET", "POST"], endpoint="login")
def login():
    if request.method == "GET":
        return render_template("login.html", )
    else:
        user = request.form.get("user")
        pwd = request.form.get("pwd")

        if user == "alex" and pwd == "123":
            session["user_info"] = user
            return redirect("/index")
        else:
            return render_template("login.html", **{"msg": "erro"})


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


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


if __name__ == '__main__':
    app.run(port=5001)
后续会添加自定义seesion
7. Flask 中的 蓝图

小型的目录结构:

蓝图


# _*_ coding:utf-8 _*_
# __author__Zj__

from flask import Blueprint, render_template 

# 各个蓝图 view 里面的code

account = Blueprint("account", __name__, template_folder="templates",url_prefix="/account") 
# 可重新 定义templates的文件夹,并且可以设置url的分组前缀(重要)


@account.route("/")

def index():

    return render_template("actest.html")

大型的目录结构:
大型蓝图

各个文件夹下的__init__方法的code


# -*- coding:utf-8 -*-

from flask import Blueprint

admin = Blueprint(
    'admin',
    __name__,
    template_folder='templates',
    static_folder='static'
)
from . import views # 视图函数 

各个文件夹下的view 函数文件

# -*- coding:utf-8 -*-

from . import admin


@admin.route('/index')
def index():
    return 'Admin.Index'
8. flash (message) 闪现

message是一个基于Session实现的用于保存数据的集合,其特点是:使用一次就删除。

# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask, flash, get_flashed_messages, request, redirect

app = Flask(__name__)
app.debug = True
app.secret_key = "1111"


@app.route("/")
def index():
    val = request.get_data("page")

    if val == 'page':
        return "page"


    else:
        flash("page not get !", category="erro") # 向flash中添加 messgae ,可以指定 category的类型,以便获取

        return redirect("/erro")


@app.route("/erro")
def erro():

    eval = get_flashed_messages(category_filter="erro")  
    # 是一个 列表的形式  取category 放进去的值
    
    print(eval)

    return "错误信息:" + eval[0]


if __name__ == '__main__':
    app.__call__()
    app.run()
9.中间件
9.1 基于flask 装饰器的中间件

before_request after_request

# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask, render_template, request, redirect, session, url_for

app = Flask(__name__)
app.debug = True
app.secret_key = "123"
# 请求到来之前做session验证,不需要使用装饰器
# 请求扩展

USERS = {
    1: {'name': '老张', 'age': 18, 'gender': '男',
        'text': "当眼泪掉下来的时候,是真的累了, 其实人生就是这样: 你有你的烦,我有我的难,人人都有无声的泪,人人都有难言的苦。 忘不了的昨天,忙不完的今天,想不到的明天,走不完的人生,过不完的坎坷,越不过的无奈,听不完的谎言,看不透的人心放不下的牵挂,经历不完的酸甜苦辣,这就是人生,这就是生活。"},
    2: {'name': '狼王', 'age': 28, 'gender': '男',
        'text': "高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"},
    3: {'name': '老鸨', 'age': 18, 'gender': '女',
        'text': "高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"},
}

# 每次请求到来之前 执行before_request 装饰器函数,实现session的验证
@app.before_request 
def process_request(*args,**kwargs):
    print(request.path)
    if request.path == '/':
        return  # 返回none 证明 可以执行 下面的request
    user = session.get("user_info")
    print(user)
    if user:
        return
    return redirect("/")  # 返回相对应的地址


@app.before_request
def process_request1(*args, **kwargs):
    print("process_request1")


@app.before_request
def process_request2(*args, **kwargs):
    print("process_request2")


@app.route("/", methods=["GET", "POST"], endpoint="login")
def login():
    if request.method == "GET":
        return render_template("login.html", )
    else:
        user = request.form.get("user")
        pwd = request.form.get("pwd")

        if user == "alex" and pwd == "123":
            session["user_info"] = user
            return redirect("/index")
        else:
            return render_template("login.html", **{"msg": "erro"})


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


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


@app.after_request
def process_response1(response, *args, **kwargs):
    print("process_response1")
    return response


@app.after_request
def process_response2(response, *args, **kwargs):
    print("process_response2")
    return response


# before_request返回none时 执行下一个request 否则拦截 但是还是会执行response函数
# 执行顺序 request1 -> request2 -> response2 -> response1





@app.errorhandler(404)   # 定制错误信息
def erro_404(arg):
    return "404"


if __name__ == '__main__':
    app.run(port=5002)
    
    
9.2中间件的 自定制
# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask

app = Flask(__name__)
app.debug = True
# 自定义中间件

@app.route("/")
def sayhello():
    return "hello word"
    
class Md(object):

    def __init__(self, old_wsgi_app):
        self.old_wsgi_app = old_wsgi_app

    def __call__(self, environ, start_response):
        # 实例化的时候运行call 方法
        print("before")
        ret = self.old_wsgi_app(environ, start_response)
        print("after")
        return ret
if __name__ == '__main__':
    # 这一步 是进行实例化把  
    Flask——>app.wsgi_app
    传递进来,接着就执行call方法 其实就是执行app.wsgi_app(参数)
    app.wsgi_app = Md(app.wsgi_app)  # 执行 Md  __call__ 方法

    app.run(port=9000)
    

app 的 call 方法源码

def wsgi_app(self, environ, start_response):
      
        ctx = self.request_context(environ)  # requestCountext
        ...........

def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)
        

猜你喜欢

转载自www.cnblogs.com/zjcode/p/9129187.html