記錄安裝過程與新項目搭建過程
摘取官網中比較重要的部分翻譯
幾個比較好的項目推薦:
可參考教程:https://github.com/Microndgt/The-Flask-Mega-Tutorial
https://www.jianshu.com/p/b44ae3ffa1b6
https://www.codingpy.com/article/flask-web-development-book-pdf/
對應視頻:https://www.bilibili.com/video/av5708093
電商系統:https://github.com/371854496/flask-vue-xcx-
仿網易雲https://blog.csdn.net/AA_LiuIsMe_AA/article/details/90181132
源碼:https://download.csdn.net/download/aa_liuisme_aa/11176525
音樂推薦系統:
https://github.com/caodaqian/Music-Recommend/blob/master/Src/App/models.py
音樂庫管理系統:
https://github.com/SixSen/music-website-flask/blob/master/app/home/views.py
Table of Contents
前言:
配置和慣例
剛起步的時候 Flask 有許多帶有合理缺省值的配置值和慣例。按照慣例, 模板和靜態文件存放在應用的 Python 源代碼樹的子目錄中,名稱分別爲 templates 和 static 。慣例是可以改變的,但是你大可不必改變, 尤其是剛起步的時候。
Flask 包含許多可以自定義其行爲的鉤子。考慮到你的定製需求, Flask 的類專爲繼承 而打造。
安裝:
依賴會自動安裝。
Werkzeug 用於實現 WSGI ,應用和服務之間的標準 Python 接口。
Jinja 用於渲染頁面的模板語言。
MarkupSafe 與 Jinja 共用,在渲染頁面時用於避免不可信的輸入,防止注入攻擊。
ItsDangerous 保證數據完整性的安全標誌數據,用於保護 Flask 的 session cookie.
Click 是一個命令行應用的框架。用於提供 flask 命令,並允許添加自定義 管理命令。
快速上手
一個最小的 Flask 應用如下:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
那麼,這些代碼是什麼意思呢?
1.首先我們導入了 Flask 類。 該類的實例將會成爲我們的 WSGI 應用。
2.接着我們創建一個該類的實例。第一個參數是應用模塊或者包的名稱。如果你使用 一個單一模塊(就像本例),那麼應當使用 __name__ ,因爲名稱會根據這個 模塊是按應用方式使用還是作爲一個模塊導入而發生變化(可能是 ‘__main__’ , 也可能是實際導入的名稱)。這個參數是必需的,這樣 Flask 才能知道在哪裏可以 找到模板和靜態文件等東西。更多內容詳見 Flask 文檔。
3.然後我們使用 route() 裝飾器來告訴 Flask 觸發函數的 URL 。
4.函數名稱被用於生成相關聯的 URL 。函數最後返回需要在用戶瀏覽器中顯示的信息。
外部可見的服務器
運行服務器後,會發現只有你自己的電腦可以使用服務,而網絡中的其他電腦卻 不行。缺省設置就是這樣的,因爲在調試模式下該應用的用戶可以執行你電腦中 的任意 Python 代碼。
如果你關閉了調試器或信任你網絡中的用戶,那麼可以讓服務器被公開訪問。 只要在命令行上簡單的加上 --host=0.0.0.0 即可:
$ flask run --host=0.0.0.0
這行代碼告訴你的操作系統監聽所有公開的 IP 。
路由
使用 route() 裝飾器來把函數綁定到 URL:
@app.route('/hello') def hello(): return 'Hello, World' |
變量規則
通過把 URL 的一部分標記爲 <variable_name> 就可以在 URL 中添加變量。標記的 部分會作爲關鍵字參數傳遞給函數。通過使用 <converter:variable_name> ,可以 選擇性的加上一個轉換器,爲變量指定規則。請看下面的例子:
@app.route('/user/<username>') def show_user_profile(username): # show the user profile for that user return 'User %s' % escape(username)
@app.route('/post/<int:post_id>') def show_post(post_id): # show the post with the given id, the id is an integer return 'Post %d' % post_id |
string
(缺省值) 接受任何不包含斜槓的文本
int
接受正整數
float
接受正浮點數
path
類似 string ,但可以包含斜槓
uuid
接受 UUID 字符串
以下兩條規則的不同之處在於是否使用尾部的斜槓。:
@app.route('/projects/')
def projects():
return 'The project page'
@app.route('/about')
def about():
return 'The about page'
projects 的 URL 是中規中矩的,尾部有一個斜槓,看起來就如同一個文件夾。 訪問一個沒有斜槓結尾的 URL 時 Flask 會自動進行重定向,幫你在尾部加上一個斜槓。
about 的 URL 沒有尾部斜槓,因此其行爲表現與一個文件類似。如果訪問這個 URL 時添加了尾部斜槓就會得到一個 404 錯誤。這樣可以保持 URL 唯一,並幫助 搜索引擎避免重複索引同一頁面。
URL 構建
url_for() 函數用於構建指定函數的 URL。它把函數名稱作爲第一個 參數。它可以接受任意個關鍵字參數,每個關鍵字參數對應 URL 中的變量。未知變量 將添加到 URL 中作爲查詢參數。
from flask import Flask, escape, url_for
app = Flask(__name__)
@app.route('/') def index(): return 'index' @app.route('/user/<username>') def profile(username): return '{}\'s profile'.format(escape(username))
with app.test_request_context(): print(url_for('index')) print(url_for('profile', username='John Doe')) |
HTTP 方法¶
Web 應用使用不同的 HTTP 方法處理 URL 。當你使用 Flask 時,應當熟悉 HTTP 方法。 缺省情況下,一個路由只回應 GET 請求。 可以使用 route() 裝飾器的 methods 參數來處理不同的 HTTP 方法:
from flask import request
@app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': return do_the_login() else: return show_the_login_form() |
靜態文件
使用特定的 'static' 端點就可以生成相應的 URL
url_for('static', filename='style.css')
這個靜態文件在文件系統中的位置應該是 static/style.css 。
渲染模板
Flask 自動爲你配置 Jinja2 模板引擎。
使用 render_template() 方法可以渲染模板,你只要提供模板名稱和需要 作爲參數傳遞給模板的變量就行了。下面是一個簡單的模板渲染例子:
from flask import render_template
@app.route('/hello/') @app.route('/hello/<name>') def hello(name=None): return render_template('hello.html', name=name) |
Flask 會在 templates 文件夾內尋找模板。因此,如果你的應用是一個模塊, 那麼模板文件夾應該在模塊旁邊;如果是一個包,那麼就應該在包裏面:
情形 1 : 一個模塊:
/application.py
/templates
/hello.html
情形 2 : 一個包:
/application
/__init__.py
/templates
/hello.html
模板在繼承使用的情況下尤其有用。其工作原理參見 模板繼承 方案文檔。簡單的說,模板繼承可以使每個頁面的特定元素(如頁頭、導航和頁尾) 保持一致。
自動轉義默認開啓。因此,如果 name 包含 HTML ,那麼會被自動轉義。如果你可以 信任某個變量,且知道它是安全的 HTML (例如變量來自一個把 wiki 標記轉換爲 HTML 的模塊),那麼可以使用 Markup 類把它標記爲安全的,或者在模板 中使用 |safe 過濾器。更多例子參見 Jinja 2 文檔。
下面 Markup 類的基本使用方法:
>>> from flask import Markup >>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>' Markup(u'<strong>Hello <blink>hacker</blink>!</strong>') |
操作請求數據
在 Flask 中由全局 對象 request 來提供請求信息。如果你有一些 Python 基礎,那麼 可能 會奇怪:既然這個對象是全局的,怎麼還能保持線程安全?答案是本地環境
本地環境
這個只有在做單元測試時纔有用。在測試 時會遇到由於沒有請求對象而導致依賴於請求的代碼會突然崩潰的情況。對策是自己創建 一個請求對象並綁定到環境。最簡單的單元測試解決方案是使用 test_request_context() 環境管理器。通過使用 with 語句 可以綁定一個測試請求,以便於交互。
請求對象
首先,你必須從 flask 模塊導入請求對象:
from flask import request |
通過使用 method 屬性可以操作當前請求方法,通過使用 form 屬性處理表單數據(在 POST 或者 PUT 請求 中傳輸的數據)。以下是使用上述兩個屬性的例子:
@app.route('/login', methods=['POST', 'GET']) def login(): error = None if request.method == 'POST': if valid_login(request.form['username'], request.form['password']): return log_the_user_in(request.form['username']) else: error = 'Invalid username/password' # the code below is executed if the request method # was GET or the credentials were invalid return render_template('login.html', error=error) |
當 form 屬性中不存在這個鍵時會發生什麼?會引發一個 KeyError 。 如果你不像捕捉一個標準錯誤一樣捕捉 KeyError ,那麼會顯示一個 HTTP 400 Bad Request 錯誤頁面。因此,多數情況下你不必處理這個問題。
要操作 URL (如 ?key=value )中提交的參數可以使用 args 屬性:
searchword = request.args.get('key', '') |
用戶可能會改變 URL 導致出現一個 400 請求出錯頁面,這樣降低了用戶友好度。因此, 我們推薦使用 get 或通過捕捉 KeyError 來訪問 URL 參數。
文件上傳
用 Flask 處理文件上傳很容易,只要確保不要忘記在你的 HTML 表單中設置 enctype="multipart/form-data" 屬性就可以了。否則瀏覽器將不會傳送你的文件。
from flask import request
@app.route('/upload', methods=['GET', 'POST']) def upload_file(): if request.method == 'POST': f = request.files['the_file'] f.save('/var/www/uploads/uploaded_file.txt') |
文件上傳之前其在客戶端系統中的名稱,可以使用 filename 屬性。
f.save('/var/www/uploads/' + secure_filename(f.filename)) |
Cookies
要訪問 cookies ,可以使用 cookies 屬性。可以使用響應 對象 的 set_cookie 方法來設置 cookies 。請求對象的 cookies 屬性是一個包含了客戶端傳輸的所有 cookies 的字典。在 Flask 中,如果使用session ,那麼就不要直接使用 cookies ,因爲 session比較安全一些。
使用 延遲的請求回調 方案可以在沒有響應對象的情況下設置一個 cookie 。
重定向和錯誤
使用 redirect() 函數可以重定向。使用 abort() 可以 更早退出請求,並返回錯誤代碼:
from flask import abort, redirect, url_for
@app.route('/') def index(): return redirect(url_for('login'))
@app.route('/login') def login(): abort(401) this_is_never_executed() |
它讓一個用戶從索引頁重定向到一個無法訪問的頁面(401 表示禁止訪問)
缺省情況下每種出錯代碼都會對應顯示一個黑白的出錯頁面。使用 errorhandler() 裝飾器可以定製出錯頁面:
from flask import render_template
@app.errorhandler(404) def page_not_found(error): return render_template('page_not_found.html'), 404 |
關於響應
視圖函數的返回值會自動轉換爲一個響應對象。如果返回值是一個字符串,那麼會被 轉換爲一個包含作爲響應體的字符串、一個 200 OK 出錯代碼 和一個 text/html 類型的響應對象。以下是轉換的規則:
- 如果視圖返回的是一個響應對象,那麼就直接返回它。
- 如果返回的是一個字符串,那麼根據這個字符串和缺省參數生成一個用於返回的 響應對象。
- 如果返回的是一個字典,那麼調用 jsonify 創建一個響應對象。
- 如果返回的是一個元組,那麼元組中的項目可以提供額外的信息。元組中必須至少 包含一個項目,且項目應當由 (response, status) 、 (response, headers) 或者 (response, status, headers) 組成。 status 的值會重載狀態代碼, headers 是一個由額外頭部值組成的列表 或字典。
- 如果以上都不是,那麼 Flask 會假定返回值是一個有效的 WSGI 應用並把它轉換爲 一個響應對象。
如果想要在視圖內部掌控響應對象的結果,那麼可以使用 make_response() 函數。
@app.errorhandler(404) def not_found(error): return render_template('error.html'), 404 |
@app.errorhandler(404) def not_found(error): resp = make_response(render_template('error.html'), 404) resp.headers['X-Something'] = 'A value' return resp |
JSON 格式的 API
如果從視圖 返回一個 dict ,那麼它會被轉換爲一個 JSON 響應。
@app.route("/me") def me_api(): user = get_current_user() return { "username": user.username, "theme": user.theme, "image": url_for("user_image", filename=user.image), } |
如果 dict 還不能滿足需求,還需要創建其他類型的 JSON 格式響應,可以使用 jsonify() 函數。該函數會序列化任何支持的 JSON 數據類型。
@app.route("/users") def users_api(): users = get_all_users() return jsonify([user.to_json() for user in users]) |
會話
除了請求對象之外還有一種稱爲 session 的對象,允許你在不同請求 之間儲存信息。這個對象相當於用密鑰簽名加密的 cookie ,即用戶可以查看你的 cookie ,但是如果沒有密鑰就無法修改它。
使用session之前你必須設置一個密鑰。舉例說明:
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
# Set the secret key to some random bytes. Keep this really secret! app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
@app.route('/') def index(): if 'username' in session: return 'Logged in as %s' % escape(session['username']) return 'You are not logged in'
@app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': session['username'] = request.form['username'] return redirect(url_for('index')) return ''' <form method="post"> <p><input type=text name=username> <p><input type=submit value=Login> </form> '''
@app.route('/logout') def logout(): # remove the username from the session if it's there session.pop('username', None) return redirect(url_for('index')) |
這裏用到的 escape() 是用來轉義的。如果不使用模板引擎就可以像上例 一樣使用這個函數來轉義。
如何生成一個好的密鑰 :
生成隨機數的關鍵在於一個好的隨機種子,因此一個好的密鑰應當有足夠的隨機性。 操作系統可以有多種方式基於密碼隨機生成器來生成隨機數據。使用下面的命令 可以快捷的爲 Flask.secret_key ( 或者 SECRET_KEY )生成值:
$ python -c 'import os; print(os.urandom(16))'
基於 cookie 的session的說明: Flask 會取出會話對象中的值,把值序列化後儲存到 cookie 中。在打開 cookie 的情況下,如果需要查找某個值,但是這個值在請求中 沒有持續儲存的話,那麼不會得到一個清晰的出錯信息。
除了缺省的客戶端會話之外,還有許多 Flask 擴展支持服務端會話。
消息閃現
閃現系統的基本工作原理是在請求結束時 記錄一個消息,提供且只提供給下一個請求使用。通常通過一個佈局模板來展現閃現的 消息。
flash() 用於閃現一個消息。在模板中,使用 get_flashed_messages() 來操作消息。
日誌
以下是一些日誌調用示例:
app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')
logger 是一個標準的 Logger Logger 類, 更多信息詳見官方的 logging 文檔。
音樂庫管理系統:
https://github.com/SixSen/music-website-flask/blob/master/app/home/views.py
解決github下不下來:https://www.cnblogs.com/USTC-ZCC/p/11163292.html
碼雲這幾年發展很快啊
新建music_website的py3.7虛擬環境
使用requirements安裝:https://www.cnblogs.com/imzhizi/p/python-pip-requirementstxt-wen.html
pip install -r requirements.txt |
若安裝超時使用鏡像:
https://blog.csdn.net/weixin_34722995/article/details/71751837?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
在 pip 後面跟-i 來指定源,比如用豆瓣的源來安裝 whois庫: pip install whois -i http://pypi.douban.com/simple |
可以在使用pip的時候加參數-i https://pypi.tuna.tsinghua.edu.cn/simple 例如:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyspider,這樣就會從清華這邊的鏡像去安裝pyspider庫。 |
運行準備:導入sql,修改model與init、home/view(pymysql)中的連接數據庫爲flask_website
啓動項目:https://blog.csdn.net/qq_40648358/article/details/104410698
python manage.py runserver python manage.py runserver -p port -h host |
瀏覽器查看:http://localhost:5000
不錯,裏面存的歌都是我愛聽的
修改
使用bootstrap-glyphicons 字體圖標:
https://www.runoob.com/bootstrap/bootstrap-glyphicons.html
註冊登陸常用提示:
Sign up
XX can't be blank
Email is invalid
Confirm the password
Enter the password again
The username has been registered
設置的菜單在menu
歌曲榜單菜單在fav
Py2->py3:
print->print()
cPickle-> import pickle as pk
拆分後view中無法使用app
File "D:\anacondaProject\music-website-flask\app\home\views.py", line 70, in welcome app.result = result NameError: name 'app' is not defined |
解決方法:
from flask import Blueprint, current_app
heysyni = Blueprint('heysyni', __name__)
@heysyni.route('/heysyni/') def aheysyni(): #Got my app here app = current_app._get_current_object() |
Flask應用在處理客戶端請求(request)時,會在當前處理請求的線程中推送(push)一個上下文實例和請求實例(request),請求結束時就會彈出(pop)請求實例和上下文實例,所以current_app和request是具有相同的生命週期的,且是綁定在當前處理請求的線程上的。
如果一個沒有推送上下文實例就直接使用current_app,會報錯
參考:https://www.javaear.com/question/9946136.html
https://segmentfault.com/q/1010000005155007
https://www.ctolib.com/docs-flaskcn-c-151014.html
https://segmentfault.com/q/1010000005865632/a-1020000005865704/revision#r6
https://www.cnblogs.com/angrycode/p/11456932.html
至此疑惑已解決。
導入pickle時報錯UnicodeDecodeError: 'ascii' codec can't decode byte 0x?? in position 1: ordinal not in range(128)
py2中存儲的pickle和py3中pickle無法讀取的兼容性問題解決方案
參考:https://blog.csdn.net/zjuPeco/article/details/78449371
https://blog.csdn.net/accumulate_zhang/article/details/78597823
import pickle with open("factor_solve_data.pkl",'rb') as f: factor_data = pickle.load(f, encoding='latin1') |
則報錯TypeError: cannot use a string pattern on a bytes-like object
參考:
https://www.zhangshengrong.com/p/boNwZZnMaw/
import pickle class StrToBytes: def __init__(self, fileobj): self.fileobj = fileobj def read(self, size): return self.fileobj.read(size).encode() def readline(self, size=-1): return self.fileobj.readline(size).encode()
read = open('XXX.pkl', 'r') data = pickle.load(StrToBytes(read),encoding='iso-8859-1')
print(data) |
使用: class StrToBytes: def __init__(self, fileobj): self.fileobj = fileobj def read(self, size): return self.fileobj.read(size).encode() # return self.fileobj.read(size).decode() def readline(self, size=-1): return self.fileobj.readline(size).encode() # return self.fileobj.readline(size).decode()
#模型pickle def load_pickle(): with open('data/pickles/where2go_model.pkl', 'r') as data_file: # with open('data/pickles/where2go_model.pkl', 'rb') as data_file: data_dict = pkl.load(StrToBytes(data_file), encoding='iso-8859-1') return data_dict |
報錯cannot use a string pattern on a bytes-like object
File "D:\anacondaProject\music-website-flask\app\home\views.py", line 80, in userinput ms = app.where2go.most_similar(data) File "D:\anacondaProject\music-website-flask\app\where2go_model.py", line 132, in most_similar terms = self.parse_search_query(input) File "D:\anacondaProject\music-website-flask\app\where2go_model.py", line 78, in parse_search_query string = re.sub('-\s*', '+-', string) |
py3傳個參數還變成bytes了呢
轉換成string:str()
報錯:"word 'b'beijing'' not in vocabulary
File "D:\anacondaProject\music-website-flask\app\home\views.py", line 81, in userinput ms = app.where2go.most_similar(data) File "D:\anacondaProject\music-website-flask\app\where2go_model.py", line 144, in most_similar master_vector = multiplier * self.model[word] |
b'beijing'可能是byte->str過程中多出來的前綴
bytes轉無前綴str,參考:https://blog.csdn.net/qq_21153619/article/details/84841184
str(b'haha', encoding='utf-8') bytes.decode(b'haha') |
只在第一次報過的錯,但第二次運行就好了
*****一大堆<--top_places_json end 127.0.0.1 - - [17/Mar/2020 02:13:16] "POST /map HTTP/1.1" 500 - return self.view_functions[rule.endpoint](**req.view_args) File "D:\anacondaProject\music-website-flask\app\home\views.py", line 85, in userinput app.result['top_places'] = top_places_json TypeError: 'NoneType' object does not support item assignment |
在mvc中使用urlfor找引用路徑
Py3字符串截取:
print str[6:] #截取第七個字符到結尾(即截掉前6個)static/banners |
File "D:\anacondaProject\music-website-flask\app\templates\home\welcome.html", line 77, in template var popupContent = '<a target="_blank" class="popup" href="' + feature.properties.url + '">' + '<div class=crop> <img src="{{ url_for(\'static\',filename=\''+feature.properties.image+'\') }}' + + '" height/></div><div class=text-center style="padding:15px 0 0 0"><font size="5">' + feature.properties.title + '</font></div></a>'; jinja2.exceptions.TemplateSyntaxError: unexpected char '\\' at expected token ',', got 'static' |
參考:https://blog.csdn.net/qq_39241986/article/details/80680392
在flask中使用urlfor: https://segmentfault.com/q/1010000012038984
如果需要去掉這個錯誤,你需要使用url_for,但是在單獨的js文件裏面,url_for是無法被解析的。
href = "{{url_for('card_detail', card_no=card_no) }}" 在js中寫爲 "{{url_for('card_detail')}}?card_no=" + car_no |
"{{url_for('card_detail')}}?card_no=" + car_no "{{ url_for(‘static’)}}?filename="+feature.properties.image |
均無效,嘗試搭建後臺接口傳圖片,但flask對https://localhost:8080之類的前綴會默認修改爲flask端口+當前路由,嘗試使用flask路由返回文件
參考:https://blog.csdn.net/xw_2_xh/article/details/96175571
https://www.cnblogs.com/yaoqingzhuan/p/10997201.html
# -*- coding: utf-8 -*- from flask import Flask, render_template, send_file, send_from_directory,json, jsonify,make_response
app = Flask(__name__) #實例化flask app
#file_name是客戶端傳來的需要下載的文件名 @app.route('/get_file/<file_name>', methods=['GET']) def get_file(file_name): directory = config.APP_PATH try: response = make_response( send_from_directory(directory, file_name, as_attachment=True)) return response except Exception as e: return jsonify({"code": "異常", "message": "{}".format(e)})
if __name__ == '__main__': app.run(debug=False, host='0.0.0.0', port=80) |
from flask import Flask, send_from_directory
app = Flask(__name__)
@app.route("/download") def index(): return send_from_directory("D:\desk\id",filename="123.jpg",as_attachment=True)
if __name__ == '__main__': print(app.url_map) app.run(host="10.205.1.15", port=5000) |
使用這個要注意路由前綴雖然相同,但要改函數名
http://localhost:8080/banners/default.png
報錯send_from_directory' is not defined
參考:
|
還是404
搜索:
page_data = Music.query.filter( Music.music_name.ilike('%' + key + '%') ).order_by( Music.listen.desc() ) page_data.key = key |
<h4>{{ v.music_name }}</h4> <a href="{{ url_for('home.play',id=v.music_id) }}" class="label label-primary pull-right"><span class="glyphicon glyphicon-play"></span> Detail </a> |
Flask頁面繼承:
{% extends "home/home.html" %}
{% block username %} {{ name }} {% endblock %} |
extends代表繼承頁面
在父頁面用block ***挖空,在子頁面block ***填上(一一對應)
收藏url:
{{ url_for('home.like',id=v.music_id) }} |