Flask Web 開發 【 Chapter 7 】大型程序結構

【 Chapter 7 】大型結構目錄

7.1 項目結構

Flask 程序基本的結構:

#多文件 Flask 程序的基本結構

|-flasky                                
  |-app/                            # Flask 程序一般保存在 app 的包中;
    |-templates/
    |-static/
    |-main/
      |-__init__.py
      |-errors.py
      |-forms.py
      |-views.py
    |-__init__.py
    |-email.py
    |-models.py
  |-migrations                          # migrations 文件夾包含數據庫遷移腳本;
  |-tests/                              # tests 包中包含單元測試;
    |-__init__.py
    |-test*.py
  |venv/                                # 包含 Python 虛擬環境;
  |-requirements.txt                     # requirements.txt 列出所有的依賴包,便于在其他電腦上生成相同的                                       虛擬環境設置;
  |-config.py                            # config.py 存儲配置;
  |-manage.py                            # manage.py 用于啟動程序以及其他的程序任務;

下面講解把 hello.py 配置成如上的結構的過程。

7.2 配置選項

配置 config.py

import os

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


class Config:
    # 靈活、安全配置參數,并提供了默認值
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
    MAIL_SERVER = os.environ.get('MAIL_SERVER', 'ssmtp.qq.com')
    MAIL_PORT = int(os.environ.get('MAIL_PORT', '465'))
    MAIL_USE_SSL = os.environ.get('MAIL_USE_SSL', 'true').lower() in \
                   ['true', 'on', '1']
    # 發送者郵箱
    MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
    # 授權碼
    MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
    # 郵件標題
    FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
    # 發送者郵箱
    FLASKY_MAIL_SENDER = 'Flasky Admin <flasky@example.com>'
    # 管理員郵箱
    FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
    # 每次請求提交后,自動提交數據庫的修改
    SQLALCHEMY_TRACK_MODIFICATIONS = False

    # 配置類可以定義 init_app() 類方法,其參數是程序實例。在這個方法中,可以執行對當前環境的配置初始化。
    @staticmethod
    def init_app(app):
        pass

#開發配置類
class DevelopmentConfig(Config):
    #調試階段標記
    DEBUG = True
    # 初始化以及配置一個簡單的 SQLlite 數據庫
    # 設置 SQLite 數據庫 URI
    SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
                              'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')


class TestingConfig(Config):
    #測試階段標記
    TESTING = True
    SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or \
                              'sqlite://'

#產品配置類
class ProductionConfig(Config):
    #產品數據庫
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
                              'sqlite:///' + os.path.join(basedir, 'data.sqlite')

# 注冊了不同的配置環境,并注冊了一個默認配置
config = {
    'development': DevelopmentConfig,
    'testing': TestingConfig,
    'production': ProductionConfig,

    'default': DevelopmentConfig
}

  • 基類 Config 中包含通用配置,子類分別定義專用的配置。
  • 為了讓配置方式更靈活且更安全,某些配置可以從環境變量中導入。如: SECRET_KEY
  • 在 3 個子類中,「 SQLALCHEMY_DATABASE_URI 」 變量都被指定了不同的值。這樣程序就可在不同的配置環境中運行,每個環境都使用不同的數據庫。
  • 配置類可以定義 init_app() 類方法,其參數是程序實例。在這個方法中,可以執行對當前環境的配置初始化。現在,基類 Config 中的 init_app() 方法為空。

7.3 解析程序包

程序包用來保存程序的所有代碼、模板和靜態文件。我們可以把這個包直接稱為 app(應 用),如果有需求,也可使用一個程序專用名字。

文件目錄結構展示

models.py: 程序中的數據庫模型

from . import db


class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    users = db.relationship('User', backref='role', lazy='dynamic')

    def __repr__(self):
        return '<Role %r>' % self.name


class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True, index=True)
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))

    def __repr__(self):
        return '<User %r>' % self.username

email.py :程序匯總的電子郵件支持

from threading import Thread
from flask import current_app, render_template
from flask_mail import Message
from . import mail


def send_async_email(app, msg):
    with app.app_context():
        mail.send(msg)


def send_email(to, subject, template, **kwargs):
    app = current_app._get_current_object()
    msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' ' + subject,
                  sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
    msg.body = render_template(template + '.txt', **kwargs)
    msg.html = render_template(template + '.html', **kwargs)
    thr = Thread(target=send_async_email, args=[app, msg])
    thr.start()
    return thr

./app/init.py :創建不同配置的 app 的工廠函數

from flask import Flask
from flask_bootstrap import Bootstrap
from flask_mail import Mail
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
from config import config

bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()


def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    bootstrap.init_app(app)
    mail.init_app(app)
    moment.init_app(app)
    db.init_app(app)

    from .main import main as main_blueprint
    app.register_blueprint(main_blueprint)

    return app


./app/main/init.py : 創建藍本

from flask import Blueprint

main = Blueprint('main', __name__)

from . import views, errors

./app/main/errors.py : 錯誤處理模板

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

./app.main/views.py : 路由、視圖函數函數

from flask import render_template, session, redirect, url_for, current_app
from .. import db
from ..models import User
from ..email import send_email
from . import main
from .forms import NameForm


@main.route('/', methods=['GET', 'POST'])
def index():
    form = NameForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.name.data).first()
        if user is None:
            user = User(username=form.name.data)
            db.session.add(user)
            session['known'] = False
            if current_app.config['FLASKY_ADMIN']:
                send_email(current_app.config['FLASKY_ADMIN'], 'New User',
                           'mail/new_user', user=user)
        else:
            session['known'] = True
        session['name'] = form.name.data
        return redirect(url_for('.index'))
    return render_template('index.html',
                           form=form, name=session.get('name'),
                           known=session.get('known', False))

tempalates 文件夾 :包含程序中的 Jinja2 模板文件

static 文件夾 :包含程序中的靜態文件

./tests/test_basics.py

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

./manage.py : 啟動腳本

import os
from app import create_app,db
from app.models import User,Role
from flask_script import Manager,Shell
from flask_migrate import Migrate,MigrateCommand

app = create_app(os.getenv('Flask_CONFIG')or 'default')
manager = Manager(app)
migrate = Migrate(app,db)

def make_shell_context():
    return dict(app=app,db=db,User=User,Role=Role)
manager.add_command("shell",Shell(make_context=make_shell_context()))
manager.add_command('db',MigrateCommand)

if __name__ == 'main':
    manager.run()

./requirements.txt : 記錄索引的依賴包及版本號

pip freeze > requirements.txt 生成賴文件
pip install -r requirements.txt 生成新的虛擬環境

其實也就是把 hello.py 各個功能的方法分成不同的 python 文件,再把其它的板塊分門別類的放好,最后創建 manage.py 文件用來啟動這個項目。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • 22年12月更新:個人網站關停,如果仍舊對舊教程有興趣參考 Github 的markdown內容[https://...
    tangyefei閱讀 35,200評論 22 257
  • 把一個小應用程序的代碼都放在一起會很方便,但是不利于擴展,尤其當項目開始變大時在一個文件中工作就會帶來一些問題。不...
    tangyefei閱讀 6,895評論 3 15
  • 如果我沒有遇見你, 我會在哪里, 我該會過著什么樣的生活呢? 如果,生命里的那兩條線不會相交, 我便不會遇見你, ...
    Angel李子汐閱讀 384評論 27 28
  • 西溫的鼓勵咨詢父母成長小組在浪漫的五月前一天正式啟動啦! 在如月老師的輕聲細語引導下,八位同學開始了這個讓人期待已...
    愛是永恒閱讀 426評論 0 0