Day05 大型程序結(jié)構(gòu)

源代碼: 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è)備與你電腦的耦合嘶居。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市促煮,隨后出現(xiàn)的幾起案子食听,更是在濱河造成了極大的恐慌,老刑警劉巖污茵,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異葬项,居然都是意外死亡泞当,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)民珍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)襟士,“玉大人盗飒,你說(shuō)我怎么就攤上這事÷穑” “怎么了逆趣?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)嗜历。 經(jīng)常有香客問(wèn)我宣渗,道長(zhǎng),這世上最難降的妖魔是什么梨州? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任痕囱,我火速辦了婚禮,結(jié)果婚禮上暴匠,老公的妹妹穿的比我還像新娘鞍恢。我一直安慰自己,他們只是感情好每窖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布帮掉。 她就那樣靜靜地躺著,像睡著了一般窒典。 火紅的嫁衣襯著肌膚如雪蟆炊。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天崇败,我揣著相機(jī)與錄音盅称,去河邊找鬼。 笑死后室,一個(gè)胖子當(dāng)著我的面吹牛缩膝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播岸霹,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼疾层,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了贡避?” 一聲冷哼從身側(cè)響起痛黎,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎刮吧,沒(méi)想到半個(gè)月后湖饱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡杀捻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年井厌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡仅仆,死狀恐怖器赞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情墓拜,我是刑警寧澤港柜,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站咳榜,受9級(jí)特大地震影響夏醉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贿衍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一授舟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贸辈,春花似錦释树、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至嘴拢,卻和暖如春桩盲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背席吴。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工赌结, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人孝冒。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓柬姚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親庄涡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子量承,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 22年12月更新:個(gè)人網(wǎng)站關(guān)停,如果仍舊對(duì)舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,182評(píng)論 22 257
  • flask是python的一個(gè)web應(yīng)用框架穴店,django很多人聽(tīng)過(guò)撕捍,flask比較少見(jiàn),連創(chuàng)始人一開(kāi)始寫(xiě)出來(lái)只是...
    思而憂閱讀 2,943評(píng)論 0 5
  • 前言 筆者之前未接觸過(guò) Python泣洞,只是略懂一點(diǎn)前端忧风,所以說(shuō)從零開(kāi)始也相差無(wú)幾吧。Flask 是一個(gè)輕量級(jí)的基于...
    KenChoi閱讀 72,234評(píng)論 7 71
  • 把一個(gè)小應(yīng)用程序的代碼都放在一起會(huì)很方便球凰,但是不利于擴(kuò)展阀蒂,尤其當(dāng)項(xiàng)目開(kāi)始變大時(shí)在一個(gè)文件中工作就會(huì)帶來(lái)一些問(wèn)題该窗。不...
    tangyefei閱讀 6,883評(píng)論 3 15
  • 第七章 大型程序架構(gòu) 雖然在一個(gè)腳本里完成一個(gè)web應(yīng)用很便利,但是這也意味著它很難擴(kuò)展蚤霞。當(dāng)程序不斷增長(zhǎng),越來(lái)越復(fù)...
    易木成華閱讀 918評(píng)論 0 1