源代碼: https://github.com/ltoddy/flask-tutorial
技術(shù)交流群:630398887(歡迎一起吹牛)
文中提到的狗書(shū),就是《Flask Web開(kāi)發(fā) 基于Python的Web應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)》, 看過(guò)的人都是到,這本書(shū)坑挺多的.
就是這本,反正大家都叫狗書(shū),我也就跟著叫了……
參照狗書(shū)的內(nèi)容,以及響應(yīng)Dijkstra的模塊化程序設(shè)計(jì),我們這次要改一下程序結(jié)構(gòu),做一次大手術(shù).
本篇會(huì)好好的說(shuō)明一下藍(lán)本(也叫藍(lán)圖).
藍(lán)圖簡(jiǎn)單一點(diǎn)就是,我們之前的程序不都是有一個(gè)Flask的實(shí)例
app = Flask(__name__)
也就是這個(gè)變量app,它可以來(lái)定義路由.藍(lán)圖就是可以將路由分門(mén)別類(lèi),然后在組合在一起.
不懂沒(méi)關(guān)系,往下看.
我們需要重新設(shè)置一下項(xiàng)目結(jié)構(gòu):
.
├── app
│ ├── admin
│ │ ├── errors.py
│ │ ├── forms.py
│ │ ├── __init__.py
│ │ └── views.py
│ ├── __init__.py
│ ├── main
│ │ ├── forms.py
│ │ ├── __init__.py
│ │ └── views.py
│ ├── models.py
│ ├── static
│ └── templates
│ ├── 404.html
│ ├── 500.html
│ ├── admin
│ │ ├── login.html
│ │ └── register.html
│ ├── base.html
│ ├── index.html
│ └── user.html
├── config.py
├── manage.py
差不多是這個(gè)樣子的.
- Flask 程序一般都保存在名為app的包中.
- config.py 保存著配置
- manage.py 用于啟動(dòng)項(xiàng)目
這里說(shuō)明一下python 的包(package),從目錄結(jié)構(gòu)上看,python的package有兩部分組成: 文件夾和init.py 文件. 正是因?yàn)?strong>init.py 的存在 python編譯器才會(huì)把那個(gè)文件夾當(dāng)作是一個(gè)python的包來(lái)看待. 而那個(gè) init.py 的效果就是, 能夠有一個(gè)與包名字相同的文件. 什么意思呢匿又?
比如 我們有一個(gè)名字為 main 的包, 那么
from main import *
這行代碼中,從main包中import所有的東西,你想啊,main是個(gè)包,import進(jìn)來(lái)是啥丑念??十气? 其實(shí)阿,是import進(jìn)來(lái)的是init.py中的內(nèi)容.
把原先那個(gè)blog.py中的東西復(fù)制一下就好了.
config.py
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
SECRET_KEY = 'a string'
# 數(shù)據(jù)庫(kù)配置
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
SQLALCHEMY_COMMIT_TEARDOWN = True
SQLALCHEMY_TRACK_MODIFICATIONS = False
@staticmethod
def init_app(app):
pass
仿照狗書(shū),創(chuàng)建一個(gè)程序工廠函數(shù). 設(shè)計(jì)模式中有一個(gè)模式叫做:工廠模式,比如你需要一個(gè)東西,但這個(gè)東西你需要配置很多,這樣你就可以用到工廠模式,在把配置(比如汽車(chē)廠,把各個(gè)零件組裝起來(lái))的活交給工廠,那么工廠出來(lái)的產(chǎn)品就是好的產(chǎn)品.這樣可以降低程序的耦合度,怎么理解呢,如果這個(gè)產(chǎn)品是壞的,那么你也不需要到處去程序的代碼,只需要去那個(gè)工廠的程序中去尋找bug就好了.
程序工廠函數(shù):
說(shuō)一下工廠函數(shù),我們之前單個(gè)文件開(kāi)發(fā)程序很方便,但卻有個(gè)很大的缺點(diǎn),因?yàn)槌绦蛟谌肿饔弥袆?chuàng)建,所以無(wú)法動(dòng)態(tài)修改配置.運(yùn)行腳本時(shí),實(shí)例已經(jīng)創(chuàng)建,再修改配置為時(shí)已晚,解決問(wèn)題的方法就是延遲創(chuàng)建程序?qū)嵗?把創(chuàng)建過(guò)程移到可顯式調(diào)用的工廠函數(shù)中.
<small>app.__init__.py</small>
from flask import Flask
from flask.ext.bootstrap import Bootstrap
from flask.ext.sqlalchemy import SQLAlchemy
from config import Config
bootstrap = Bootstrap()
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
Config.init_app(app)
bootstrap.init_app(app)
db.init_app(app)
return app
看一下這段代碼,我們的bootstrap和db實(shí)例,先于app創(chuàng)建,app的創(chuàng)建只能調(diào)用了create_app()函數(shù)之后才創(chuàng)建.
藍(lán)圖
Flask 中的藍(lán)圖為這些情況設(shè)計(jì):
- 把一個(gè)應(yīng)用分解為一個(gè)藍(lán)圖的集合雅倒。這對(duì)大型應(yīng)用是理想的。一個(gè)項(xiàng)目可以實(shí)例化一個(gè)應(yīng)用對(duì)象,初始化幾個(gè)擴(kuò)展允趟,并注冊(cè)一集合的藍(lán)圖。
- 以 URL 前綴和/或子域名鸦致,在應(yīng)用上注冊(cè)一個(gè)藍(lán)圖潮剪。 URL 前綴/子域名中的參數(shù)即成為這個(gè)藍(lán)圖下的所有視圖函數(shù)的共同的視圖參數(shù)(默認(rèn)情況下)。
- 在一個(gè)應(yīng)用中用不同的 URL 規(guī)則多次注冊(cè)一個(gè)藍(lán)圖分唾。
- 通過(guò)藍(lán)圖提供模板過(guò)濾器抗碰、靜態(tài)文件、模板和其它功能绽乔。一個(gè)藍(lán)圖不一定要實(shí)現(xiàn)應(yīng)用或者視圖函數(shù)弧蝇。
- 初始化一個(gè) Flask 擴(kuò)展時(shí),在這些情況中注冊(cè)一個(gè)藍(lán)圖。
藍(lán)圖問(wèn)題,最后總結(jié).往下看.
在藍(lán)本中定義的路由處于休眠狀態(tài),直到藍(lán)本注冊(cè)到程序上后,路由才真正成為程序的一部分.
創(chuàng)建藍(lán)本:
<small>app/main/__init__</small>
from flask import Blueprint
main = Blueprint('main', __name__)
from . import views, errors
藍(lán)圖是通過(guò)實(shí)例化Blueprint類(lèi)對(duì)象來(lái)創(chuàng)建的看疗。這個(gè)類(lèi)的構(gòu)造函數(shù)接收兩個(gè)參數(shù):藍(lán)圖名和藍(lán)圖所在的模塊或包的位置沙峻。與應(yīng)用程序一樣,在大多數(shù)情況下鹃觉,對(duì)于第二個(gè)參數(shù)值使用Python的name變量即可专酗。
應(yīng)用程序的路由都保存在app/main/views.py模塊內(nèi)部,而錯(cuò)誤處理程序則保存在app/main/errors.py中盗扇。導(dǎo)入這些模塊可以使路由祷肯、錯(cuò)誤處理與藍(lán)圖相關(guān)聯(lián)。重要的是要注意疗隶,在app/init.py腳本的底部導(dǎo)入模塊要避免循環(huán)依賴佑笋,因?yàn)関iew.py和errors.py都需要導(dǎo)入main藍(lán)圖。
注冊(cè)藍(lán)圖:
<small>app/__init__.py</small>
from flask import Flask
from flask.ext.bootstrap import Bootstrap
from flask.ext.sqlalchemy import SQLAlchemy
from config import Config
bootstrap = Bootstrap()
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
Config.init_app(app)
bootstrap.init_app(app)
db.init_app(app)
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
app.register_blueprint(main_blueprint)
通過(guò)register_blueprint方法將藍(lán)圖注冊(cè)進(jìn)來(lái).
<small>app/main/errors.py</small>
from flask import render_template
from . import main
@main.app_errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@main.app_errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
<small>app/main/views.py</small>
from flask import render_template
from flask import session
from flask import redirect
from flask import url_for
from . import main
from .forms import NameForm
from ..models import User
from .. import db
@main.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
user = User.query.filter_by(name=form.name.data)
if user is None:
user = User(name=form.name.data)
db.session.add(user)
session['known'] = False
else:
session['known'] = True
session['name'] = form.name.data
form.name.data = ''
return redirect(url_for('main.index'))
# 這里注意一下,我們這里要寫(xiě)main.index,因?yàn)槲覀冞@個(gè)視圖函數(shù)隸屬于main這個(gè)藍(lán)本,
# main.index是他完整的名字.
return render_template('index.html',
form=form, name=session.get('name'),
known=session.get('known', False))
<small>app/main/forms.py</small>
from flask.ext.wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import Required
class NameForm(FlaskForm):
name = StringField('你叫什么名字', validators=[Required()])
submit = SubmitField('提交')
<small>manage.py</small>
from flask.ext.script import Manager
from app import create_app, db
app = create_app()
manager = Manager(app)
if __name__ == '__main__':
manager.run()
我們運(yùn)行一下:
打開(kāi)Pycharm為我們集成好的終端(Terminal)
python3 manage.py shell
>>> from manage import *
>>> db
<SQLAlchemy engine=sqlite:////home/me/PycharmProjects/blog/data.sqlite>
>>> db.drop_all()
>>> db.create_all()
>>> exit()
先把我們的表重新創(chuàng)建一下,因?yàn)楸硪呀?jīng)移動(dòng)位置了.
然后
python3 manage.py runserver
就可以看到程序正常運(yùn)行了.
好像看不出藍(lán)本有啥用……好吧斑鼻。
現(xiàn)在還看不出,下一篇就確實(shí)看的出來(lái)了,因?yàn)槲覀円押笈_(tái)搭建好,然后在后臺(tái)把博客文章提交上去,然后文章存到數(shù)據(jù)庫(kù)里面,然后數(shù)據(jù)有了,在前端把數(shù)據(jù)顯示出來(lái).
你的筆記本上有很多接口:USB蒋纬、電源接口、SD卡槽坚弱、耳機(jī)孔蜀备、HDMI(可插拔視圖)等等;隔壁老王的電腦荒叶,就一type-C口(藍(lán)圖接口)碾阁,其他的接口只能通過(guò)type-C的擴(kuò)展塢(在藍(lán)圖中添加url規(guī)則)再接到電腦上(注冊(cè)藍(lán)圖)。老王下班些楣,直接拔了那一根type-C走人(取消注冊(cè)藍(lán)圖)脂凶,而你要拔四五根線,這時(shí)候你就發(fā)現(xiàn)了這根type-C的方便愁茁,甚至當(dāng)某個(gè)外接設(shè)備出問(wèn)題(業(yè)務(wù)邏輯需要修改)時(shí)蚕钦,你只需要在外接設(shè)備與那個(gè)拓展塢(藍(lán)圖中)之間修復(fù),基本沒(méi)你電腦(主程序)什么事鹅很,因?yàn)樗档土似渌饨釉O(shè)備與你電腦的耦合嘶居。