Web後端學習筆記 Flask(1)基礎知識

基礎的準備

1. 使用的python版本 python3.6

2. pycharm編輯器

3. 安裝python虛擬環境:

python中的虛擬環境:

python中的虛擬環境相當於一個抽屜,在這個抽屜中安裝的任何python包都不會影響到其他的抽屜。

通過pip install virtualenv來安裝python的虛擬環境,如果安裝出現下面的錯誤(Read time out,就是下載連接超時,更換到國內的鏡像源即可)

更換到國內鏡像源有兩種方法:

a. 通過安裝命令指定鏡像源

b. 同構修改python中的配置文件,永久的修改下載鏡像源

這裏選擇通過安裝命令臨時修改鏡像源:

pip install virtualenv -i https://pypi.douban.com/simple/

安裝virtualenvwrapper,這個軟件包可以使得管理虛擬環境更加的簡單,不用再到某個目錄下通過virtualenv來創建虛擬環境,並且激活的時候也不不用到特定的目錄下去

pip install virtualenvwrapper-win -i https://pypi.douban.com/simple/

virtualenvwrapper的基本使用:

1. 創建虛擬環境

mkvirtualenv flask-env

2. 切換到某個虛擬環境(進入虛擬環境)

workon flask-env

3. 退出當前虛擬環境

deactivate

4. 刪除某個虛擬環境

rmvirtualenv flask-env

5. 列出所有虛擬環境

lsvirtualenv

6. 進入虛擬環境所在的目錄

cdvirtualenv

7. 修改虛擬環境的路徑,

在環境變量->系統變量中添加一個參數WORKON_HOME,將這個參數的值設定爲你需要的路徑

8. 創建虛擬環境的時候指定python的版本‘

mkvirtualenv --python=D:/Python36/python.exe flask-env

URL組成部分詳解:

URL是Uniform Resource Locator的簡寫,統一資源定位符。

一個URL由以下幾部分組成:
scheme://host:port/path/?query-string=xxx#anchor

scheme:代表的是訪問的協議,一般爲http或者https以及ftp等。
host:主機名,域名,比如www.douban.com。
port:端口號。當你訪問一個網站的時候,瀏覽器默認使用80端口。
path:查找路徑,比如:www.jianshu.com/trending/now,後面的trending/now就是path。
query-string:查詢字符串,比如:www.baidu.com/s?wd=python,後面的wd=python就是查詢字符串。
anchor:錨點,後臺一般不用管,前端用來做頁面定位的。比如:

Web服務器:負責處理http請求,響應靜態文件,常見的有Nginx

應用服務器:負責處理邏輯的服務器,例如編寫的python代碼,是不能直接通過nginx這種web服務器來處理的,只能通過應用服務器來處理,常見的應用服務器有uwsgi、tomcat等。

web應用框架:一般使用某種語言,封裝了常用的web功能的框架就是web應用框架,flask、Django框架都是web應用框架。

Flask簡介:

flask是一款非常流行的python web框架,它是一個微框架,具有簡潔的特點,且flask和相關的依賴設計的非常優秀。

打開創建的虛擬環境:創建第一個flask程序

from flask import Flask

app = Flask(__name__)
# __name__參數的所用:
# 1. 可以規定模板和靜態文件的查找路徑
# 2. 以後如果flask的插件,比如flask-migrate, flask-sqlalchemy如果報錯,
# 那麼flask可以通過這個參數找到具體的錯誤位置

# @app.route()的作用是將url映射到對應的視圖函數上面
@app.route('/')
def hello_world():
    return 'Hello vv World!'


if __name__ == '__main__':
    app.run(port=8000)     # app.run()是flask中的一個測試服務器


flask的debug模式

在pycharm中,在程序中開始debug=True之後,需要在IDE中下面的位置:

 點擊Edit Configurations, 然後再勾選FLASK_DEBUG,這樣就可以開啓debug模式

1. 開始debug模式,在代碼中如果拋出了異常,在瀏覽器的頁面中可以看到具體錯誤信息以及錯誤代碼

2. 如果不開啓debug模式,則看不到相應的錯誤信息

3. 開啓debug模式之後,再修改完代碼後,不用再重新手動運行代碼,按下CTRL+S,flask會自動將修改後的代碼加載。此時只需要重新刷新網頁即可。

flask中的配置文件

一般再開發中,我們會將整個項目的配置放到一個文件中,這樣會便於整個項目的管理,可以在flask目錄下新建一個config.py文件,用於存放整個項目的所有配置:

1. 首先import config

app.config.from_object(config)加載配置文件

2. 在不想import config.py文件的時候,可以通過另一種方式導入:

參數silent=false:如果加載的配置文件不存在,就會報錯,如果silent=true,則即使加載的文件不存在,也不會報錯

app.config.from_pyfile("config.py", silent=False)   # 加載配置文件config, 也可以加載普通的txt文件

---------------------------------------------------------------------------------------------

URL與視圖函數的映射

如何在url中傳遞參數:

在flask中,通過裝飾器app.route()將url與視圖函數映射起來

from flask import Flask

app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False)   # 加載配置文件config, 也可以加載普通的txt文件


@app.route('/')
def hello_world():
    return 'Hello World!'


@app.route('/list/')
def article_list():
    return "article list"


if __name__ == '__main__':
    app.run(debug=True)     # 開啓debug模式

如果需要在URL中傳遞參數,則可以按照如下的方式進行:同時視圖函數也必須定義同名的形參

@app.route('/article/<article_id>/')   # url最好以/結尾
def article_detail(article_id):
    return "The article id is %s" % article_id

如果傳遞的參數的數據類型是某一個確定的類型,則可以進行指定:

@app.route('/article/<int:article_id>/')   # url最好以/結尾
def article_detail(article_id):
    return "The article id is %s" % article_id

其他的數據類型還有:

還有一種特殊的數據類型: uuid  通用唯一識別碼(Universally Unique Identifier)

這種字符串中的每一個能夠保證唯一性,所以可以作爲數據庫中的主鍵進行使用,在python中可以通過uuid模塊生成uuid

import uuid
print(uuid.uuid4())

爲了保證UUID的唯一性,規範定義了包括網卡MAC地址、時間戳、名字空間(Namespace)、隨機或僞隨機數、時序等元素,以及從這些元素生成UUID的算法。UUID的複雜特性在保證了其唯一性的同時,意味着只能由計算機生成。UUID是不能人工指定的,除非你冒着UUID重複的風險。UUID的複雜性決定了“一般人“不能直接從一個UUID知道哪個對象和它關聯。

例如,在某網站需要訪問博客或者用戶,那麼可以按照一般的方法,定義兩個視圖函數

# .../blog/<id>/    訪問博客

#.../user/<id>/      訪問某一位作者

還可以按照另一種方式實現,將兩個url放到一個當中,可以通過any()實現

@app.route('/<any(blog, user):url_pass>/<id>/')   # url_pass用於存放blog或者user
def get_detail(url_pass, id):
    if url_pass == "blog":
        return "You get blog detail %s" % id
    else:
        return "You get user detail %s" % id

此時:

http://127.0.0.1:5000/blog/12/

http://127.0.0.1:5000/user/12/

兩個鏈接均可以映射到同一個視圖函數

總結:

傳遞參數的數據類型可以總結爲:
int, float, string, path, uuid, any

獲取客戶端發送的參數

在get方法中,用戶會通過查詢字符串的方式向服務端傳送數據,在服務端,flask可以按照以下的方式獲取參數

from flask import Flask
from flask import request

app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False)   # 加載配置文件config, 也可以加載普通的txt文件


@app.route('/')
def hello_world():
    return 'Hello World!'


@app.route('/day/')
def get_day():
    username = request.args.get("user")
    passwd = request.args.get("password")
    return "User: %s password: %s" % (username, passwd)

    
if __name__ == '__main__':
    app.run(debug=True)     # 開啓debug模式

則通過url獲取的參數如下:

所以接受用戶傳遞的參數有兩種方式:
1. 通過path的形式,將參數嵌入到路徑當中,進行參數傳遞

2. 使用查詢字符串的方式,即通過“?key=value”的形式

採用第一種形式有利於搜索引擎優化(SEO),而第二種方式不利於搜索引擎優化

flask: url_for使用詳解:

app.route()用於將url映射到視圖函數,而url_for則正好與之相反,可以通過視圖函數找到對應的url

from flask import Flask
from flask import url_for

app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False)   # 加載配置文件config, 也可以加載普通的txt文件


@app.route('/')
def hello_world():
    print(url_for("my_list"))   # 視圖函數對應的url  輸出/list/
    return 'Hello World!'


@app.route('/list/')
def my_list(page):
    return "My list is here %d" % page 


if __name__ == '__main__':
    app.run(debug=True)     # 開啓debug模式

還可以獲取url中具有參數的視圖函數的url:

from flask import Flask
from flask import url_for

app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False)   # 加載配置文件config, 也可以加載普通的txt文件


@app.route('/')
def hello_world():
    print(url_for("my_list", page=1))   # 視圖函數對應的url  輸出/list/
    return 'Hello World!'


@app.route('/list/<page>')
def my_list(page):
    return "My list is here %d" % page


if __name__ == '__main__':
    app.run(debug=True)     # 開啓debug模式

url_for的優點:

1. 如果修改了url,就不用去手動拼接替換url

2. url_for會自動處理特殊字符,例如\,會自動對\進行編碼,不用去手動處理

url_for中的next參數,相當於給url中添加一個查詢字符串,字符串的形式爲 ?next="xxx"

自定義url轉換器

例如,在一個url中傳遞的參數中有手機號碼,爲了對手機號碼這一參數進行嚴格的限制,需要自定義url轉換器:

所有上述介紹的轉換器的定義都在一個稱爲BaseConverter的包中:

from werkzeug.routing import BaseConverter

在這個包中,不同的轉換器都是繼承自基類BaseConverter

例如:

class UnicodeConverter(BaseConverter):
    """This converter is the default converter and accepts any string but
    only one path segment.  Thus the string can not include a slash.

    This is the default validator.

    Example::

        Rule('/pages/<page>'),
        Rule('/<string(length=2):lang_code>')

    :param map: the :class:`Map`.
    :param minlength: the minimum length of the string.  Must be greater
                      or equal 1.
    :param maxlength: the maximum length of the string.
    :param length: the exact length of the string.
    """

    def __init__(self, map, minlength=1, maxlength=None, length=None):
        BaseConverter.__init__(self, map)
        if length is not None:
            length = "{%d}" % int(length)
        else:
            if maxlength is None:
                maxlength = ""
            else:
                maxlength = int(maxlength)
            length = "{%s,%s}" % (int(minlength), maxlength)
        self.regex = "[^/]" + length

class NumberConverter(BaseConverter):
    """Baseclass for `IntegerConverter` and `FloatConverter`.

    :internal:
    """

    weight = 50

    def __init__(self, map, fixed_digits=0, min=None, max=None, signed=False):
        if signed:
            self.regex = self.signed_regex
        BaseConverter.__init__(self, map)
        self.fixed_digits = fixed_digits
        self.min = min
        self.max = max
        self.signed = signed

    def to_python(self, value):
        if self.fixed_digits and len(value) != self.fixed_digits:
            raise ValidationError()
        value = self.num_convert(value)
        if (self.min is not None and value < self.min) or (
            self.max is not None and value > self.max
        ):
            raise ValidationError()
        return value

    def to_url(self, value):
        value = self.num_convert(value)
        if self.fixed_digits:
            value = ("%%0%sd" % self.fixed_digits) % value
        return str(value)

    @property
    def signed_regex(self):
        return r"-?" + self.regex


class IntegerConverter(NumberConverter):
    """This converter only accepts integer values::

        Rule("/page/<int:page>")

    By default it only accepts unsigned, positive values. The ``signed``
    parameter will enable signed, negative values. ::

        Rule("/page/<int(signed=True):page>")

    :param map: The :class:`Map`.
    :param fixed_digits: The number of fixed digits in the URL. If you
        set this to ``4`` for example, the rule will only match if the
        URL looks like ``/0001/``. The default is variable length.
    :param min: The minimal value.
    :param max: The maximal value.
    :param signed: Allow signed (negative) values.

    .. versionadded:: 0.15
        The ``signed`` parameter.
    """

    regex = r"\d+"
    num_convert = int


class FloatConverter(NumberConverter):
    """This converter only accepts floating point values::

        Rule("/probability/<float:probability>")

    By default it only accepts unsigned, positive values. The ``signed``
    parameter will enable signed, negative values. ::

        Rule("/offset/<float(signed=True):offset>")

    :param map: The :class:`Map`.
    :param min: The minimal value.
    :param max: The maximal value.
    :param signed: Allow signed (negative) values.

    .. versionadded:: 0.15
        The ``signed`` parameter.
    """

    regex = r"\d+\.\d+"
    num_convert = float

    def __init__(self, map, min=None, max=None, signed=False):
        NumberConverter.__init__(self, map, min=min, max=max, signed=signed)

系統中已經預定義的轉換器如下所示:

#: the default converter mapping for the map.
DEFAULT_CONVERTERS = {
    "default": UnicodeConverter,
    "string": UnicodeConverter,
    "any": AnyConverter,
    "path": PathConverter,
    "int": IntegerConverter,
    "float": FloatConverter,
    "uuid": UUIDConverter,
}

所以,可以按照轉換器的定義方法,自定義手機號碼轉換器,定義一個視圖函數,並且將電話號碼作爲參數傳入到url中:

from flask import Flask
from werkzeug.routing import BaseConverter
import re

app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False)   # 加載配置文件config, 也可以加載普通的txt文件


# 自定義轉換器
class TelephoneConverter(BaseConverter):
    regex = r'1[0-9]{9}[0-9]'


app.url_map.converters["tel"] = TelephoneConverter    # 將自定義的轉換器添加到預定義的轉換器中


@app.route('/')
def hello_world():
    return 'Hello World!'


@app.route('/telephone/<tel:phone>/')
def user_phone(phone):
    return "The user phone is %s" % phone


if __name__ == '__main__':
    app.run(debug=True)     # 開啓debug模式

總結:

1. 實現自定義轉換器,必須實現一個類,繼承自基類BaseConverter

2. 在自定義的類中,必須重寫正則表達式regex

3. 將自定義的類映射到app.url_map.converters["tel"] =  xxx

      轉換器除了對傳入到url中的參數的數據類型可以限制外,還具有真正的轉換功能,例如,對傳入到url中的參數進行解析並返回,此時,如果對url傳入某種格式的參數,那個url對應的視圖函數此時接受到的參數,實際上是經過Converter轉換過的參數,可以直接使用。例如,對於 /blog/ai+c/表示需要查找分類爲AI和C語言的所有博客:此時,就可以利用Converter對傳入的參數ai+c進行轉換,使得視圖函數得到的參數爲[ai, c],需要實現這種功能,需要在Converter中實現to_python()方法:Converter中還有一個方法,即to_url(),這個方法實現的功能正好與to_python()方法相反,通過實現to_url方法,能夠獲取到帶參數的視圖函數對應的url

from flask import Flask, url_for
from werkzeug.routing import BaseConverter

app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False)   # 加載配置文件config, 也可以加載普通的txt文件


# 自定義轉換器
class TelephoneConverter(BaseConverter):
    regex = r'1[0-9]{9}[0-9]'


# 自定義轉換器
class ListConverter(BaseConverter):
    def to_python(self, value):      # 需要實現轉換功能,必須實現to_python方法,將url中的參數經過解析傳遞給視圖函數
        if "+" in value:
            res = value.split("+")
        else:
            res = [value]
        return res

    def to_url(self, value):         # 會在調用url_for的時候生成符合要求的url形式
        return "+".join(value)


app.url_map.converters["tel"] = TelephoneConverter    # 將自定義的轉換器添加到預定義的轉換器中
app.url_map.converters["list"] = ListConverter        # List 轉換器


@app.route('/')
def hello_world():
    print(url_for("get_blog", category=["aa", "bb"]))
    return 'Hello World!'


@app.route('/telephone/<tel:phone>/')
def user_phone(phone):
    return "The user phone is %s" % phone


@app.route('/blog/<list:category>/')
def get_blog(category):
    res = ""
    for x in category:
        res = res + x + "|"
    return "The blog %s" % res[:-1]


if __name__ == '__main__':
    app.run(debug=True)     # 開啓debug模式

轉換的結果:

例如,輸入的url中包含參數,/a+b/,轉換器可以對這個參數提前進行解析,使得傳遞給視圖函數的是解析過的參數[a, b]

to_url的效果:

例如視圖函數get_blog( ),通過url_for生成他的url是,可以將傳入的參數進行轉換,在嵌入到url中,得到的url中包含/a+b/

其他的小細節:

1. 在局域網中讓其他電腦能訪問到我的網站:
    修改host="0.0.0.0"

2. 指定端口號

    flask項目,默認使用的是5000端口,可以通過port=8000來設置端口號

3. url唯一性

    在定義url的時候,要在最後加上/,同時搜索引擎會將加/和不加/的url視爲兩個url,但實際上這是同一個url,因此這樣不利於SEO

4. get請求與post請求

    get: 只會在服務器上獲取資源,不會影響服務器的狀態,這種請求方式推薦使用get請求。get請求會將請求參數放到url當中 。

    post: 會給服務器提交一些數據以及文件,會對服務器的狀態產生影響。這種條件下推薦post請求

5. flask中,默認的請求方式爲GET請求,可以通過methods參數進行設置

重定向:

重定向分爲永久重定向和暫時重定向,在頁面上體現的操作就是瀏覽器會從一個頁面跳轉到另一個頁面。比如在訪問一個網站的過程中,用戶訪問了一個需要登陸的頁面,當時用戶當前沒有登陸,所以需要給用戶跳轉到登陸界面,讓其重新登陸纔可以。

永久重定向:http的狀態碼是301,多用於舊的網址被廢棄,用戶需要轉到一個新的網址。例如在瀏覽器訪問www.jingdong.com,會被永久重定向到www.jd.com,因爲網址www.jingdong.com已被廢棄。

暫時重定向:http的狀態碼是302,表示頁面暫時性的跳轉了,比如訪問一個需要權限的網址,如果用戶當前沒有登陸,應該暫時重定向到登陸頁面。

在flask中,通過redirect(locaton, code=302)來實現重定向的。

from flask import Flask, url_for, request, redirect

app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False)   # 加載配置文件config, 也可以加載普通的txt文件


@app.route('/')
def hello_world():
    return 'Hello Wold!'


@app.route('/login/')   # 登陸頁面
def login():
    return "Login page"


@app.route('/profile/')  # 用戶詳情頁
def profile():
    name = request.args.get("name")
    if name:
        return "User: %s" % name
    else:
        return redirect(url_for("login"), code=302)


if __name__ == '__main__':
    app.run(debug=True)     # 開啓debug模式

視圖函數Response返回值詳解:

視圖函數應該返回什麼樣的值?

視圖函數的返回值會被自動轉化爲一個響應對象,flask的轉化過程如下:

1. 如果返回值是Response對象,則直接返回

2. 返回值是一個字符串,那麼flask會創建一個werkzeug.wrappers.Response對象,Response將該字符串作爲主體,狀態碼爲200,MIME類型爲text/html.然後返回該Response對象

3. 如果返回的是一個元組,元組中的數據類型是(response,status,headers)。status值會覆蓋默認的200狀態碼,headers可以是一個列表或者字典,作爲額外的消息頭。

4. 如果以上條件都不滿足,flask會假設返回值是一個合法的WSGI應用程序,並通過Response.force_type()轉換爲一個請求對象。

flask = werkzeug(處理網絡請求相關的) + sqlalchemy(處理數據庫相關) + jinja2(html模板相關)

自定義響應:

Restful-API都是通過JSON的形式進行傳遞的,如果後臺跟前臺進行交互的時候,所有的url都是發送JSON數據,則可以定義一個叫做JSONResponse的類來代替Flask自帶的Response類。

自定義響應必須滿足以下三個條件:

1. 必須繼承自Response類

2. 必須實現force_type(cls, rv, environ=None)

3.必須指定app.response_class爲自定義的Response

4. 如果視圖函數返回的數據不是字符串,也不是元組,也不是Response對象,那麼就會將返回值傳遞給force_type,然後再將response_type的返回值返回給前端。

from flask import Flask, Response, jsonify

app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False)   # 加載配置文件config, 也可以加載普通的txt文件


# 將視圖函數中返回的字典,轉換爲JSON對象,然後返回
class JSONResponse(Response):
    @classmethod
    def force_type(cls, response, environ=None):
        """
        這個方法只有視圖函數返回,非字符串,非元組,非Response對象的時候纔會調用
        :param response:
        :param environ:
        :return:
        """
        if isinstance(response, dict):
            # jsonify將字典轉換爲json對象,且包裝成Response對象
            response = jsonify(response)
        return super(JSONResponse, cls).force_type(response, environ)


app.response_class = JSONResponse


@app.route('/')
def hello_world():
    return 'Hell Wold!'


@app.route('/list/')
def list_1():
    return {"name": "lili", "age": 26}     # 此時返回的是字典,所以會調用上面的JSONResponse方法


if __name__ == '__main__':
    app.run(debug=True)     # 開啓debug模式

視圖函數可以返回的類型:

1. 可以返回字符串,flask底層會將這個字符串包裝成Response對象

2. 可以返回元組,元組的形式是:(響應體,狀態碼,頭部信息),返回的元組其實在底層也是封裝成Response對象

3. 可以返回Response及其子類

------------------------------------------------------------------------------------------------------------------------------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章