天天看點

Python WEB架構之Flask

前言:

Django:1個重武器,包含了web開發中常用的功能、元件的架構;(ORM、Session、Form、Admin、分頁、中間件、信号、緩存、ContenType....);

Tornado:2大特性就是異步非阻塞、原生支援WebSocket協定;

Flask:封裝功能不及Django完善,性能不及Tornado,但是Flask的第三方開源元件比豐富;http://flask.pocoo.org/extensions/

Bottle:比較簡單;

總結:

都不是我寫的!!!不論優劣,不同的工具而已;

小型web應用設計的功能點不多使用Flask;

大型web應用設計的功能點比較多使用的元件也會比較多,使用Django(自帶功能多不用去找插件);

如果追求性能可以考慮Tornado;

Flask的socket是基于Werkzeug 實作的,模闆語言依賴jinja2模闆,在使用Flask之前需要安裝一下;

pip3 install flask           #安裝flask      
Python WEB架構之Flask
Python WEB架構之Flask
from werkzeug.wrappers import Request, Response  # Flask的socket使用werkzeug實作,是以要導入 werkzeug

@Request.application

def hellow(request):
    return Response('Hello World')

if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('localhost',400,hellow)      
Python WEB架構之Flask

Flask簡單使用

Python WEB架構之Flask
Python WEB架構之Flask
from flask import Flask

app=Flask(__name__) #建立1個Flask執行個體

@app.route('/')      #路由系統生成 視圖對應url,1. decorator=app.route() 2. decorator(first_flask)
def first_flask():    #視圖函數
    return 'Hello World'  #response


if __name__ == '__main__':
    app.run()              #啟動socket      
Python WEB架構之Flask

一、配置檔案

app=Flask(__name__,template_folder='templates',static_url_path='/static/',static_path='/zhanggen')      

模闆路徑: template_folder='templates'

靜态檔案路徑:static_url_path='/static/'

靜态檔案引入别名:static_path='/zhanggen'

設定為調試環境:app.debug=True (代碼修改自動更新)

設定json編碼格式 如果為False 就不使用ascii編碼:app.config['JSON_AS_ASCII']=False 

設定響應頭資訊Content-Type   app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8"  (注意 ;charset=utf-8)

二、路由系統

1.動态路由(url傳參)

@app.route('/user/<name>')

Python WEB架構之Flask
Python WEB架構之Flask
from flask import Flask

app=Flask(__name__)

@app.route('/<name>')  #設定url傳參數 http://127.0.0.1:5000/zhanggen
def first_flask(name):  #視圖必須有對應接收參數
    print(name)
    return 'Hello World'  #response


if __name__ == '__main__':
    app.run()      
Python WEB架構之Flask

@app.route('/post/<int:age>')

Python WEB架構之Flask
Python WEB架構之Flask
#接收整型數字參數
app=Flask(__name__)
@app.route('/<int:age>/')  #設定url傳參數 http://127.0.0.1:5000/18/
def first_flask(age):  #視圖必須有對應接收參數
    print(age)
    return 'Hello World'  #response

if __name__ == '__main__':
    app.run()      
Python WEB架構之Flask

@app.route('/post/<float:salary>')

Python WEB架構之Flask
Python WEB架構之Flask
#接收浮點型型數字參數
app=Flask(__name__)
@app.route('/<float:salary>/')  #設定url傳參數http://127.0.0.1:5000/2345555.8889/
def first_flask(salary):  #視圖必須有對應接收參數
    print(salary)
    return 'Hello World'  #response

if __name__ == '__main__':
    app.run()      
Python WEB架構之Flask

@app.route('/post/<path:path>')

Python WEB架構之Flask
Python WEB架構之Flask
# 接收URL連結類型參數
app=Flask(__name__)
@app.route('/<path:url>/')  #設定url傳參數:http://127.0.0.1:5000/http://www.baiu.com/
def first_flask(url):  #視圖必須有對應接收參數
    print(url)
    return 'Hello World'  #response

if __name__ == '__main__':
    app.run()      
Python WEB架構之Flask

2、指定允許的請求方法

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

Python WEB架構之Flask
Python WEB架構之Flask
# 指定允許的請求方法
app=Flask(__name__)
@app.route('/<path:url>/',methods=['get']) #隻允許get請求
def first_flask(url):
    print(url)
    return 'Hello World'  #response

if __name__ == '__main__':
    app.run()      
Python WEB架構之Flask

3、通過别名反向生成url

Python WEB架構之Flask
Python WEB架構之Flask
#反向生成url
from flask import Flask,url_for
app=Flask(__name__)
@app.route('/<path:url>',endpoint='name1')
def first_flask(url):
    print(url_for('name1',url=url)) #如果設定了url參數,url_for(别名,加參數)
    return 'Hello World'

if __name__ == '__main__':
    app.run()      
Python WEB架構之Flask

4、通過app.add_url_rule()調用路由

Python WEB架構之Flask
Python WEB架構之Flask
#方式2通過app.add_url_rule()方法的方式調用路由
app=Flask(__name__)

def first_flask():
    return 'Hello World' 

app.add_url_rule(rule='/index/',endpoint='name1',view_func=first_flask,methods=['GET'])
#app.add_url_rule(rule=通路的url,endpoint=路由别名,view_func=視圖名稱,methods=[允許通路的方法])
if __name__ == '__main__':
    app.run()      
Python WEB架構之Flask

5、擴充路由功能:正則比對url

如果需要一些複雜的比對規則可以自定義正則比對url

Python WEB架構之Flask
Python WEB架構之Flask
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()      
Python WEB架構之Flask

四、視圖

1、給Flask視圖函數加裝飾器

注意如果要給視圖函數加裝飾器增加新功能,一點要加在路由裝飾器下面,才會被路由裝飾器裝飾,才能生生成url關系;

Python WEB架構之Flask
Python WEB架構之Flask
#給Flask視圖加裝飾器
#1、定義1個裝飾器

def auth(func):
    print('我在上面')
    def inner(*args,**kwargs):
        return func(*args,**kwargs)
    return inner


app=Flask(__name__)

@app.route('/',methods=['GET'])
@auth #注意如果要給視圖函數加裝飾器,一點要加在路由裝飾器下面,才會被路由裝飾器裝飾
def first_flask():
    print('ffff')
    return 'Hello World'

if __name__ == '__main__':
    app.run()      
Python WEB架構之Flask

2、request和response

 a.請求相關資訊

request.method: 擷取請求方法

request.json

request.json.get("json_key"):擷取json資料 **較常用      

request.argsget('name') :擷取get請求參數   

request.form.get('name') :擷取POST請求參數

request.form.getlist('name_list'):擷取POST請求參數清單(多個)

request.values.get('age') :擷取GET和POST請求攜帶的所有參數(GET/POST通用)

request.cookies.get('name'):擷取cookies資訊

request.headers.get('Host'):擷取請求頭相關資訊

request.path:擷取使用者通路的url位址,例如(/,/login/,/ index/);

request.full_path:擷取使用者通路的完整url位址+參數 例如(/login/?age=18)

request.script_root: 抱歉,暫未了解其含義;

request.url:擷取通路url位址,例如http://127.0.0.1:5000/?age=18;

request.base_url:擷取通路url位址,例如 http://127.0.0.1:5000/;

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))  直接儲存

 b、響應相關資訊

return "字元串" :響應字元串

return render_template('html模闆路徑',**{}):響應模闆

return redirect('/index.html'):跳轉頁面

響應json資料

方式1: return jsonify(user_list) 

Python WEB架構之Flask
app.config['JSON_AS_ASCII']=False  #指定json編碼格式 如果為False 就不使用ascii編碼,
app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8" #指定浏覽器渲染的檔案類型,和解碼格式;      

方式2:

return Response(data,mimetype="application/json;charset=utf-8",)

如果需要設定響應頭就需要借助make_response()方法

from flask import Flask,request,make_response                 

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 respons

3 、Flask之CBV視圖

Python WEB架構之Flask
Python WEB架構之Flask
#CBV視圖
from flask import Flask,url_for,views
#-----------------------------------------------------
app=Flask(__name__)               #裝飾器

def auth(func):
    print('我在上面')
    def inner(*args,**kwargs):
        return func(*args,**kwargs)
    return inner
#--------------------------------------------------------
class IndexView(views.MethodView):  #CBV視圖
    methods=['GET']                #允許的http請求方法(改CBV隻允許GET方法)
    decorators = [auth,]            #每次請求過來都加auth裝飾器

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

app.add_url_rule('/index/',view_func=IndexView.as_view(name='name1')) #(name='name1'反向生成url别名


if __name__ == '__main__':
    app.run()      
Python WEB架構之Flask

五、模闆語言

Flask使用的是Jinja2模闆,是以其文法和Django無差别(Django的模闆語言參考Jinja2)

1.引用靜态檔案

方式1:别名引入

<link rel="stylesheet" href="/zhanggen/commons.css">      

方式2:url_for()方法引入

<link rel="stylesheet" href="{{  url_for('static',filename='commons.css')  }}">      

2.模闆語言引用上下文對象

變量

Python WEB架構之Flask
<h1>{{user_list}}</h1> <!--變量 -->      

循環、索引取值

Python WEB架構之Flask
Python WEB架構之Flask
<ul>
{% for user in user_list %}   <!--循環 -->
    <li>{{user}}</li>
{% endfor %}

{{user_list.0}}             <!-- 索引取值-->

</ul>      
Python WEB架構之Flask

Flask的Jinjia2可以通過Context 把視圖中的函數傳遞把模闆語言中執行,這就是Django中的simple_tag和simple_fifter;

simple_tag(隻能傳2個參數,支援for、if)

Python WEB架構之Flask
@app.template_global()   #simple_tag
def foo(arg):
    return '<input type="text">'      
Python WEB架構之Flask
<h1>{{foo(1)|safe}}</h1>                   <!--Flask的模闆語言支援simple_tag-->      

simple_fifter(對參數個數無限制,不支援for、if)

Python WEB架構之Flask
@app.template_filter()  #simple_fifter
def foo1(arg1,arg2,arg3):
    return arg1+arg2+arg3      
Python WEB架構之Flask
<h1> {{ 'alex'|foo1('s ','b',) }}  </h1>     <!-- simple_fifter -->      

3.wtform(flask表單驗證插件)

3.0.簡介

 wtforms WTForms是一個支援多個web架構的form元件,主要對使用者請求資料 進行表單驗證。

3.1. 安裝

pip install wtforms  #安裝wtfroms插件      

3.2.簡單使用

wtforms和Django自帶的form驗證插件功能相同,使用起來大同小異;

使用者登入頁面驗證

Python WEB架構之Flask
Python WEB架構之Flask
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets


app=Flask(__name__,template_folder='templates') #知道模闆檔案
app.debug=True

#登入驗證執行個體
class LoginForm(Form):

    #不同的字段 内部包含正規表達式 html5.EmailField | html5.DateTimeField...
    name=simple.StringField(
        label='使用者名',
        validators=[                                #驗證規則和錯誤提示資訊
            validators.DataRequired(message='使用者名不能為空.'),
            validators.Length(min=6, max=18, message='使用者名長度必須大于%(min)d且小于%(max)d')
        ],
        widget=widgets.TextInput(),                 #前端頁面顯示的插件.TextArea
        render_kw={'class': 'form-control'}      #設定form标簽的class資訊

        )

    # 不同的字段 内部包含正規表達式  html5.EmailField | html5.DateTimeField...
    pwd = simple.PasswordField(
        label='密碼',
        validators=[
            validators.DataRequired(message='密碼不能為空.'),
            validators.Length(min=8, message='使用者名長度必須大于%(min)d'),
            #自定義驗證規則
            validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
                              message='密碼至少8個字元,至少1個大寫字母,1個小寫字母,1個數字和1個特殊字元')

        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )



@app.route('/login/', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        form = LoginForm()     #執行個體化 form驗證類
        return render_template('login.html', form=form)
    else:
        form = LoginForm(formdata=request.form)
        if form.validate(): #判斷是否驗證成功?
            print('使用者送出資料通過格式驗證,送出的值為:', form.data)
        else:
            print(form.errors)
        return render_template('login.html', form=form)

if __name__ == '__main__':
    app.run()      
Python WEB架構之Flask
Python WEB架構之Flask
Python WEB架構之Flask
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登入</h1>
<form method="post" novalidate>
    <!--<input type="text" name="name">-->
    <p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p>

    <!--<input type="password" name="pwd">-->
    <p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p>
    <input type="submit" value="送出">
</form>
</body>
</html>

login.html      
Python WEB架構之Flask

使用者注冊頁面驗證 

Python WEB架構之Flask
Python WEB架構之Flask
#使用者注冊
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets

app = Flask(__name__, template_folder='templates')
app.debug = True



class RegisterForm(Form):
    name = simple.StringField(
        label='使用者名',
        validators=[
            validators.DataRequired()
        ],
        widget=widgets.TextInput(),
        render_kw={'class': 'form-control'},
        default='張根'                                             #設定input标簽中預設值
    )

    pwd = simple.PasswordField(
        label='密碼',
        validators=[
            validators.DataRequired(message='密碼不能為空.')
        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )

    pwd_confirm = simple.PasswordField(                                #第二次輸入密碼
        label='重複密碼',
        validators=[
            validators.DataRequired(message='重複密碼不能為空.'),
            validators.EqualTo('pwd', message="兩次密碼輸入不一緻")  #驗證2次輸入的密碼是否一緻?
        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )

    email = html5.EmailField(
        label='郵箱',
        validators=[
            validators.DataRequired(message='郵箱不能為空.'),
            validators.Email(message='郵箱格式錯誤')
        ],
        widget=widgets.TextInput(input_type='email'),    #生成email input标簽
        render_kw={'class': 'form-control'}
    )

    gender = core.RadioField(
        label='性别',
        choices=(                                        #choice radio選項
            (1, '男'),
            (2, '女'),
        ),
        coerce=int                                       #講使用者送出過來的 '4' 強制轉成 int 4
    )
    city = core.SelectField(
        label='城市',
        choices=(
            ('bj', '北京'),
            ('sh', '上海'),
        )
    )

    hobby = core.SelectMultipleField(                      #select 下拉框多選框
        label='愛好',
        choices=(
            (1, '籃球'),
            (2, '足球'),
        ),
        coerce=int
    )

    favor = core.SelectMultipleField(
        label='喜好',
        choices=(
            (1, '籃球'),
            (2, '足球'),
        ),
        widget=widgets.ListWidget(prefix_label=False),        #生成Checkbox 多選框
        option_widget=widgets.CheckboxInput(),
        coerce=int,
        default=[1, 2]
    )

    def __init__(self, *args, **kwargs):                        #重寫form驗證類的__init__方法可以實時同步資料中資料
        super(RegisterForm, self).__init__(*args, **kwargs)
        self.favor.choices = ((1, '籃球'), (2, '足球'), (3, '羽毛球'))


    def validate_pwd_confirm(self, field):                       #wtforms驗證 鈎子函數
        """
        自定義pwd_confirm字段規則,例:與pwd字段是否一緻
        :param field:
        :return:
        """
        # 最開始初始化時,self.data中已經有所有的值

        if field.data != self.data['pwd']:
            # raise validators.ValidationError("密碼不一緻") # 繼續後續驗證
            raise validators.StopValidation("密碼不一緻")  # 不再繼續後續驗證


@app.route('/register/', methods=['GET', 'POST'