基礎的準備
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及其子類
------------------------------------------------------------------------------------------------------------------------------