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

源代碼: https://github.com/ltoddy/flask-tutorial

技術(shù)交流群:630398887(歡迎一起吹牛)

文中提到的狗書,就是《Flask Web開發(fā) 基于Python的Web應(yīng)用開發(fā)實戰(zhàn)》, 看過的人都是到,這本書坑挺多的.

就是這本,反正大家都叫狗書,我也就跟著叫了……

參照狗書的內(nèi)容,以及響應(yīng)Dijkstra的模塊化程序設(shè)計,我們這次要改一下程序結(jié)構(gòu),做一次大手術(shù).

本篇會好好的說明一下藍本(也叫藍圖).

藍圖簡單一點就是,我們之前的程序不都是有一個Flask的實例

app = Flask(__name__)

也就是這個變量app,它可以來定義路由.藍圖就是可以將路由分門別類,然后在組合在一起.

不懂沒關(guān)系,往下看.

我們需要重新設(shè)置一下項目結(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

差不多是這個樣子的.

  • Flask 程序一般都保存在名為app的包中.
  • config.py 保存著配置
  • manage.py 用于啟動項目

這里說明一下python 的包(package),從目錄結(jié)構(gòu)上看,python的package有兩部分組成: 文件夾和init.py 文件. 正是因為init.py 的存在 python編譯器才會把那個文件夾當作是一個python的包來看待. 而那個 init.py 的效果就是, 能夠有一個與包名字相同的文件. 什么意思呢?

比如 我們有一個名字為 main 的包, 那么

from main import *

這行代碼中,從main包中import所有的東西,你想啊,main是個包,import進來是啥??? 其實阿,是import進來的是init.py中的內(nèi)容.

把原先那個blog.py中的東西復制一下就好了.
config.py

import os

basedir = os.path.abspath(os.path.dirname(__file__))


class Config:
    SECRET_KEY = 'a string'
    # 數(shù)據(jù)庫配置
    SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
    SQLALCHEMY_COMMIT_TEARDOWN = True
    SQLALCHEMY_TRACK_MODIFICATIONS = False

    @staticmethod
    def init_app(app):
        pass

仿照狗書,創(chuàng)建一個程序工廠函數(shù). 設(shè)計模式中有一個模式叫做:工廠模式,比如你需要一個東西,但這個東西你需要配置很多,這樣你就可以用到工廠模式,在把配置(比如汽車廠,把各個零件組裝起來)的活交給工廠,那么工廠出來的產(chǎn)品就是好的產(chǎn)品.這樣可以降低程序的耦合度,怎么理解呢,如果這個產(chǎn)品是壞的,那么你也不需要到處去程序的代碼,只需要去那個工廠的程序中去尋找bug就好了.

程序工廠函數(shù):

說一下工廠函數(shù),我們之前單個文件開發(fā)程序很方便,但卻有個很大的缺點,因為程序在全局作用中創(chuàng)建,所以無法動態(tài)修改配置.運行腳本時,實例已經(jīng)創(chuàng)建,再修改配置為時已晚,解決問題的方法就是延遲創(chuàng)建程序?qū)嵗?把創(chuàng)建過程移到可顯式調(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實例,先于app創(chuàng)建,app的創(chuàng)建只能調(diào)用了create_app()函數(shù)之后才創(chuàng)建.

藍圖

Flask 中的藍圖為這些情況設(shè)計:

  • 把一個應(yīng)用分解為一個藍圖的集合。這對大型應(yīng)用是理想的。一個項目可以實例化一個應(yīng)用對象,初始化幾個擴展,并注冊一集合的藍圖。
  • 以 URL 前綴和/或子域名,在應(yīng)用上注冊一個藍圖。 URL 前綴/子域名中的參數(shù)即成為這個藍圖下的所有視圖函數(shù)的共同的視圖參數(shù)(默認情況下)。
  • 在一個應(yīng)用中用不同的 URL 規(guī)則多次注冊一個藍圖。
  • 通過藍圖提供模板過濾器、靜態(tài)文件、模板和其它功能。一個藍圖不一定要實現(xiàn)應(yīng)用或者視圖函數(shù)。
  • 初始化一個 Flask 擴展時,在這些情況中注冊一個藍圖。

藍圖問題,最后總結(jié).往下看.

在藍本中定義的路由處于休眠狀態(tài),直到藍本注冊到程序上后,路由才真正成為程序的一部分.

創(chuàng)建藍本:

<small>app/main/__init__</small>

from flask import Blueprint

main = Blueprint('main', __name__)

from . import views, errors

藍圖是通過實例化Blueprint類對象來創(chuàng)建的。這個類的構(gòu)造函數(shù)接收兩個參數(shù):藍圖名和藍圖所在的模塊或包的位置。與應(yīng)用程序一樣,在大多數(shù)情況下,對于第二個參數(shù)值使用Python的name變量即可。

應(yīng)用程序的路由都保存在app/main/views.py模塊內(nèi)部,而錯誤處理程序則保存在app/main/errors.py中。導入這些模塊可以使路由、錯誤處理與藍圖相關(guān)聯(lián)。重要的是要注意,在app/init.py腳本的底部導入模塊要避免循環(huán)依賴,因為view.py和errors.py都需要導入main藍圖。

注冊藍圖:

<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)

通過register_blueprint方法將藍圖注冊進來.

<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'))
        # 這里注意一下,我們這里要寫main.index,因為我們這個視圖函數(shù)隸屬于main這個藍本,
        # 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()

我們運行一下:
打開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)建一下,因為表已經(jīng)移動位置了.

然后

python3 manage.py runserver

就可以看到程序正常運行了.

好像看不出藍本有啥用……好吧。
現(xiàn)在還看不出,下一篇就確實看的出來了,因為我們要把后臺搭建好,然后在后臺把博客文章提交上去,然后文章存到數(shù)據(jù)庫里面,然后數(shù)據(jù)有了,在前端把數(shù)據(jù)顯示出來.

你的筆記本上有很多接口:USB、電源接口、SD卡槽、耳機孔、HDMI(可插拔視圖)等等;隔壁老王的電腦,就一type-C口(藍圖接口),其他的接口只能通過type-C的擴展塢(在藍圖中添加url規(guī)則)再接到電腦上(注冊藍圖)。老王下班,直接拔了那一根type-C走人(取消注冊藍圖),而你要拔四五根線,這時候你就發(fā)現(xiàn)了這根type-C的方便,甚至當某個外接設(shè)備出問題(業(yè)務(wù)邏輯需要修改)時,你只需要在外接設(shè)備與那個拓展塢(藍圖中)之間修復,基本沒你電腦(主程序)什么事,因為它降低了其他外接設(shè)備與你電腦的耦合。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容