[Python] Flask從0到1開發輕量級網頁
概述
- Flask採用MVT模型,即Model, Template, View
- Model:定義資料的儲存格式,並且提供了資料庫訪問的API
- View:定義那些資料被顯示,是業務邏輯處理模組
- Template:定義資料如何被顯示
例項1(簡單例項)
- 在專案資料夾建立虛擬環境,安裝依賴包
- virtualenv venv
- venv\Scripts\activate
- pip install –r requirements.txt
- 建立程式例項app,是一個Flask類的物件
- 客戶端(瀏覽器)把請求傳送給Web伺服器,Web伺服器再把請求傳送給Flask程式例項,程式例項需要知道對每個URL執行哪些程式碼,在Flask中通過app.route裝飾器定義,儲存了一個URL到Python函式的對映關係(路由)
- 路由後面的函式是檢視函式,檢視函式的返回值稱為響應,是客戶端接收到的內容
- <>中的內容為動態路由,用於生成針對個人的歡迎訊息
- run()啟動服務,啟動後進入輪詢,直到Ctrl+C停止
- Flask從客戶端收到請求時,通過檢視函式中的請求物件(上下文)儲存客戶端傳送的HTTP請求,Flask中的上下文分為程式(current_app、g)和請求(request、session)兩類
requirements.txt
1 alembic==1.0.1 2 asn1crypto==0.24.0 3 cffi==1.11.5 4 Click==7.0 5 cryptography==2.3.1 6View CodeFlask==1.0.2 7 Flask-Migrate==2.3.0 8 Flask-Script==2.0.6 9 Flask-SQLAlchemy==2.3.2 10 Flask-WTF==0.14.2 11 idna==2.7 12 ItsDangerous==1.1.0 13 Jinja2==2.10 14 Mako==1.0.7 15 MarkupSafe==1.0 16 Pillow==5.3.0 17 pycparser==2.19 18 PyMySQL==0.9.2 19 python-dateutil==2.7.3 20 python-editor==1.0.3 21 six==1.11.022 SQLAlchemy==1.2.12 23 Werkzeug==0.14.1 24 WTForms==2.2.1
- 在templates目錄中建立模板檔案
index.html
1 <h1>Hello World!</h1>
user.html
1 <h1>Hello, {{ name }}!</h1>
- 編寫主程式檔案
python.py
1 from flask import Flask, render_template 2 3 app = Flask(__name__) 4 5 @app.route('/') 6 def index(): 7 return render_template('index.html') 8 9 @app.route('/user/<name>') 10 def user(name): 11 return render_template('user.html', name=name) 12 13 if __name__ == '__main__': 14 app.run(debug=True)View Code
- 啟動專案
- python hello.py
- 瀏覽器訪問
例項2(新增css樣式,js動態效果)
hello.py
1 from flask import Flask, render_template 2 3 app = Flask(__name__) 4 5 @app.route('/') 6 def index(): 7 return render_template('index.html') 8 9 @app.route('/user/<name>') 10 def user(name): 11 return render_template('user.html', name=name) 12 13 if __name__ == '__main__': 14 app.run(debug=True)View Code
index.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>static demo</title> 6 <!-- 使用url_for載入靜態檔案 url_for第一個引數必須是'static',然後後面跟一個關鍵字引數filename='檔案路徑',從static資料夾下面開始尋找的 --> 7 <link rel="stylesheet" href="{{ url_for('static',filename='css/index.css') }}"> 8 <script src="../static/js/index.js"></script> 9 </head> 10 <body> 11 12 <p>static demo</p> 13 <div> 14 <img src="{{ url_for('static',filename='images/zhifubao.png') }}" alt="" width="500px" height="500px"> 15 </div> 16 </body> 17 </html>View Code
index.css
1 body{ 2 background: pink; 3 }View Code
index.js
1 alert('hello world!');
例項3(模板渲染,表單,validators)
- 現在的檢視函式中包含兩部分:請求改變程式狀態,生成請求的響應,這兩部分分別稱為業務邏輯和表現邏輯,當程式變大時,程式的可維護性會變差。
- 把表現邏輯移到模板函式中,模板是一個包含響應文字的檔案,其中包含用佔位符表示的動態部分,具體值需要在請求上下文中得到,替換後再返回最終的響應結果,這一過程稱為渲染模板。Flask使用Jinja2作為模板渲染引擎
- 預設情況下,Flask在程式資料夾中的templates子資料夾中尋找模板,render_template()函式的第一個引數是模板檔名,隨後的鍵值對引數是模板變數中的真實值,左邊的變數是引數名,即模板中的佔位符,右邊的變數是當前作用域中的變數,即同名引數的值
- 模板中{{ name }}表示變數,是一種特殊的佔位符,告訴模板引擎這個位置的值從渲染模板時使用的資料中獲取
- 過濾器:如:Hello, {{ name|capitalize }},以首字母大寫形式顯示
- 模板繼承:如:{%extends "base.html"%},表示本模板繼承自base.html,{% block title %}Index{% endblock %},表示重寫的部分
- 連結:url_for('static', filename='css/styles.css', _external=True),生成:http://localhost:5000/static/css/styles.css
- request.form獲取POST請求中提交的表單資料
- Flask-WTF能保護所有表單免收跨站請求偽造(Cross-Site Request Forgery,CSRF),為實現CSRF保護,Flask-WTF需要程式設定一個金鑰,Flask-WTF使用這個金鑰生成加密令牌,再用令牌驗證請求中表單資料的真偽
- app.config字典可用來儲存框架、擴充套件和程式本身的配置變數,SECRET_KEY配置通用金鑰,加密強度取決於變數值的機密程度,不同程式要使用不同的金鑰,且要確保其他人不知道你所用的字串,通常將金鑰儲存在環境變數中,而不直接寫入程式碼
- 使用Flask-WTF時,每個Web表單都由一個整合自Form的類表示,這個類定義表單中的一組欄位,每個欄位都用物件表示,欄位物件可附屬一個或多個驗證函式,以驗證使用者提交的輸入值是否符合需求
- StringField建構函式中的可選引數validators指定一個由驗證函式組成的列表,在接受使用者提交的資料之前驗證資料,驗證函式Required()確保提交的欄位不為空
index.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>static demo</title> 6 <!-- 使用url_for載入靜態檔案 url_for第一個引數必須是'static',然後後面跟一個關鍵字引數filename='檔案路徑',從static資料夾下面開始尋找的 --> 7 <link rel="stylesheet" href="{{ url_for('static',filename='css/index.css') }}"> 8 <script src="../static/js/index.js"></script> 9 </head> 10 <body> 11 12 <div class="page-header"> 13 <h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1> 14 </div> 15 <form method="POST"> 16 {{ form.hidden_tag() }} 17 {{ form.name.label }} {{ form.name() }} 18 {{ form.submit() }} 19 </form> 20 </body> 21 </html>View Code
hello.py
1 from flask import Flask, render_template 2 from flask_wtf import FlaskForm 3 from wtforms import StringField, SubmitField 4 from wtforms.validators import Required 5 6 app = Flask(__name__) 7 app.config["SECRET_KEY"] = "12345678" 8 9 class NameForm(FlaskForm): 10 name = StringField('What is your name?', 11 render_kw={ 12 "type" : "text", 13 "placeholder": "請輸入使用者名稱!", 14 "class":"validate-username", 15 "size" : 15}) 16 submit = SubmitField('Submit') 17 18 @app.route('/',methods=['GET', 'POST']) 19 def index(): 20 name = None 21 form = NameForm() 22 if form.validate_on_submit(): 23 name = form.name.data 24 form.name.data = '' 25 return render_template('index.html', form=form, name=name) 26 27 @app.route('/user/<name>') 28 def user(name): 29 return render_template('user.html', name=name) 30 31 if __name__ == '__main__': 32 app.run(debug=True)View Code
例項4(重定向,使用者會話,flash訊息)
- 提交使用者名稱後重新整理頁面,會提示重新提交表單,原因是重新整理頁面時瀏覽器會重新發送之前已經發送的最後一個請求,即再次提交表單,為避免這種情況,使用重定向作為POST請求的響應,瀏覽器收到重定向響應時,會向重定向的URL發起GET請求,顯示頁面內容,但這樣會帶來另一個問題,重定向後的頁面無法儲存之前提交的使用者名稱,解決的辦法是使用使用者會話(session)儲存使用者資料,sesseion本質上是一個Python字典,將使用者資訊儲存在使用者本地的客戶端中
- 重定向地址使用Flask提供的URL生成函式url_for()生成,引數是端點名,即路由的內部名稱,預設情況下就是相應檢視函式的名稱,即處理根地址的index()。效果和redirect('/')是一樣的
- 請求完成後,需要讓使用者知道狀態發生了變化,可使用flash()函式完成,並在模板中渲染Flash訊息
1 from flask import Flask, render_template, session, redirect, url_for 2 from flask_wtf import FlaskForm 3 from wtforms import StringField, SubmitField 4 from wtforms.validators import Required 5 6 app = Flask(__name__) 7 app.config["SECRET_KEY"] = "12345678" 8 9 class NameForm(FlaskForm): 10 name = StringField('What is your name?', 11 render_kw={ 12 "type" : "text", 13 "placeholder": "請輸入使用者名稱!", 14 "class":"validate-username", 15 "size" : 15},validators=[Required()]) 16 submit = SubmitField('Submit') 17 18 @app.route('/',methods=['GET', 'POST']) 19 def index(): 20 form = NameForm() 21 if form.validate_on_submit(): 22 session['name'] = form.name.data 23 return redirect(url_for('index')) 24 return render_template('index.html', form=form, name=session.get('name')) 25 26 @app.route('/user/<name>') 27 def user(name): 28 return render_template('user.html', name=name) 29 30 if __name__ == '__main__': 31 app.run(debug=True)View Code
例項5(資料庫互動,更改埠)
- 使用 Flask-SQLAlchemy 管理資料庫,完成ORM操作
- 模型:一個Python類對應一張表,類中屬性對應資料庫表中的列
- 資料庫操作
- filter_by():查詢
- add():新增
- delete():刪除
- 關係(兩個類中分別寫)
- users = db.relationship('User', backref='role')
- role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
- 若使用者資訊在資料庫中,則顯示歡迎資訊,若使用者不在資料庫中,則在資料庫中新增使用者
- 使用 Flask-Migrate 實現資料庫遷移,跟蹤資料庫模式的變化,然後增量式的把變化應用到資料庫中
- 在run()函式中實現服務埠的更改
hello.py
1 from flask import Flask, render_template, session, redirect, url_for, flash 2 from flask_sqlalchemy import SQLAlchemy 3 from flask_wtf import FlaskForm 4 from wtforms import StringField, SubmitField 5 from wtforms.validators import Required 6 from config import config 7 8 app = Flask(__name__) 9 app.config["SECRET_KEY"] = "12345678" 10 app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:[email protected]:3306/shop' 11 app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True 12 db = SQLAlchemy(app) 13 14 class NameForm(FlaskForm): 15 name = StringField('What is your name?', 16 render_kw={ 17 "type" : "text", 18 "placeholder": "請輸入使用者名稱!", 19 "class":"validate-username", 20 "size" : 15},validators=[Required()]) 21 submit = SubmitField('Submit') 22 23 @app.route('/',methods=['GET', 'POST']) 24 def index(): 25 form = NameForm() 26 if form.validate_on_submit(): 27 user = User.query.filter_by(username=form.name.data).first() 28 if user is None: 29 user = User(username = form.name.data) 30 db.session.add(user) 31 session['known'] = False 32 else: 33 session['known'] = True 34 session['name'] = form.name.data 35 form.name.data = '' 36 return redirect(url_for('index')) 37 return render_template('index.html',form = form, name = session.get('name'), known = session.get('known', False)) 38 39 class User(db.Model): 40 __tablename__ = 'user' 41 id = db.Column(db.Integer, primary_key=True) 42 username = db.Column(db.String(64)) 43 def __repr__(self): 44 return '<User %r>' % self.username 45 46 if __name__ == '__main__': 47 app.run( 48 host = '127.0.0.1', 49 port = 5001, 50 debug=True 51 )View Code
index.html
1 <!DOCTYPE html> 2 {% extends "base.html" %} 3 {% block page_content %} 4 <html lang="en"> 5 <head> 6 <meta charset="UTF-8"> 7 <title>static demo</title> 8 <!-- 使用url_for載入靜態檔案 url_for第一個引數必須是'static',然後後面跟一個關鍵字引數filename='檔案路徑',從static資料夾下面開始尋找的 --> 9 <link rel="stylesheet" href="{{ url_for('static',filename='css/index.css') }}"> 10 <script src="../static/js/index.js"></script> 11 </head> 12 <body> 13 14 <div class="page-header"> 15 <h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1> 16 {% if not known %} 17 <p>Pleased to meet you!</p> 18 {% else %} 19 <p>Happy to see you again!</p> 20 {% endif %} 21 </div> 22 <form method="POST"> 23 {{ form.hidden_tag() }} 24 {{ form.name.label }} {{ form.name() }} 25 {{ form.submit() }} 26 </form> 27 </body> 28 </html> 29 {% endblock %}View Code
參考
Flask教程
https://yq.aliyun.com/zt/3074776
url_for:靜態資源載入
https://www.cnblogs.com/guyuyun/p/9942859.html
https://www.jianshu.com/p/0e77f4eb650c
流程控制
https://blog.csdn.net/xujin0/article/details/96567884
css 樣式
https://www.cnblogs.com/yiluhuakai/p/8401740.html
css id class
https://www.runoob.com/css/css-id-class.html
flask jQuery
https://www.cnblogs.com/clnchanpin/p/6920103.html
flask css
https://blog.csdn.net/weixin_33595571/article/details/86608238
{%include%}
https://blog.csdn.net/xujin0/article/details/97102530
前端流程
https://blog.csdn.net/xtaydwxf1988/article/details/75043753
終止服務
https://www.itranslater.com/qa/details/2326227523467740160
表單類
https://blog.csdn.net/kylinxjd/article/details/94645086
https://www.jianshu.com/p/0ba3ec040fb2
CSRF token
https://www.cnblogs.com/mengbin0546/p/9966431.html
登入驗證
https://www.jianshu.com/p/06bd93e21945
wtforms注入樣式
https://www.cnblogs.com/haiyan123/p/8254228.html
https://greyli.com/flask-form-custom-form-style/
Flask輕量級部落格
https://blog.csdn.net/feit2417/article/details/80837297
CentOS部署
https://blog.csdn.net/weixin_43925725/article/details/86742259
GET和POST