Flask 實現小說網站 (二)
根據上一篇文章的使用包和模塊組織大型程序的方式,我們這次就按照這樣的方式,組織我們的代碼文件。如下:
|-flasky |-app/ |-templates/ |-static/ |-main/ |-__init__.py # 存放藍本 |-error.py # 自定義的錯誤視圖函數 |-forms.py # 存放表單類 |-views.py # 視圖函數 |-__init__.py |-email.py |-models.py # 存放模型類 |-migrations/ |-tests/ |-__init__.py |-test*.py |-venv/ |-requirements.txt |-config.py |-manage.py
配置
我們的項目程序大多數情況會需要設置多個配置。一般情況下我們的開發、測試和生產環境需要使用不同的資料庫,這樣彼此之間互不影響。我們不在使用上一篇文章中簡單的配置設定,我們使用層次結構的配置類。程序的配置,config.py:
import osclass Config: # 這個 SECRET_KEY 的值是一個敏感值,可以在環境變數中設定(set) SECRET_KEY = os.environ.get(SECRET_KEY) or you can guess # 。。。這裡添加需要的配置,如電子郵箱,資料庫配置等 # 定義一個 init_app() 類方法,他的參數是程序的實例,現在基類的 init_app() 方法為空。 @staticmethod def init_app(app): passclass DevelopmentConfig(Config): pass # 資料庫操作 class TestingConfig(Config): passclass ProductionConfig(Config): pass# 這個字典中註冊不同的配置環境,和一個默認的開發配置config = { cevelopment: DevelopmentConfig, testing: TestingConfig, production: ProductionConfig, default: DevelopmentConfig}
我們的 app(可以自己定義名字) 文件夾使我們的程序包,用來保存我們的程序所有代碼,模版文件(templates)和靜態文件(static)。我們把這兩個文件夾移到 app 文件夾中。
使用程序工廠函數
我們在單個文件中開發程序很方便,但卻有個很大的缺點,因為我們的程序在全局作用域中創建,所以無法動態的修改配置。運行程序時,程序實例已經創建,在修改配置為時已晚。這點在單元測試中十分重要,因為有時為了提高測試覆蓋率,必須在不同的配置環境中運行程序。所以,我們把創建程序實例移到可顯式調用的工廠函數中。
構造文件,app/__init__.py:
from flask import Flaskfrom flask_bootstrap import Bootstrapfrom flask_mail import Mailfrom config import configbootstrap = Bootstrap()mail = Mail()moment = Moment()# 我們的工廠函數def create_app(config_name): app = Flask(__name__) app.config.from_object(config[config_name]) # 調用 init_app() 可以完成初始化過程 bootstrap.init_app(app) mail.init_app(app) # ...附加路由和自定義的錯誤頁面 # ...註冊藍本 return app
藍本
在之前的視圖函數中,路由可以直接使用 app.route 裝飾器定義。但是現在,只有調用 create_app() (工廠函數)之後才能使用 app.route 裝飾器(錯誤頁面處理路由也一樣)。這個時候定義路由太晚了。
這時 Flask 的 藍本 (Blueprint)為我們提供了解決方案。藍本也可以定義路由,但是藍本定義的路由只要註冊到程序上,路由菜真的是程序一部分。使用全局作用域的藍本時,定義路由的方法和之前基本一樣。
我們創建一個文件用來保存我們的藍本,app/main/__init__.py:
# 創建藍本from flask import Blueprint# 必須指定兩個參數,藍本的名字和藍本所在的模塊,# 使用 Python __name__ 變數即可main = Blueprint(main, __name__)# 末尾導入,避免循環導入依賴,在視圖函數中還要導入藍本from . import views, errors
# ...def create_app(config_name): # ... from .main import main as main_blueprint app.register_blueprint(main_blueprint) return app
視圖函數
藍本中的錯誤處理程序,app/main/errors.py:
from flask import render_templatefrom . import main# 這裡我們沒有使用 errorhandler 裝飾器,# 如果我們想要註冊程序全聚德錯誤處理程序,我們應該使用 app_errorhandler@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 session, render_template, redirect, url_for, flash, requestfrom ..spider.getcontens import get_allzj_titlefrom ..spider.booknums import contents_number, book_numberfrom ..spider.getpost import get_posts# from ..spider.download import downloadfrom flask_cache import Cacheimport osfrom . import mainfrom .. import create_appfrom .forms import SearchFormapp = create_app(os.getenv(FLASK_CONFIG) or default)# 我們使用了』simple』類型緩存,其內部實現是Werkzeug中的SimpleCachecache = Cache(app, config={CACHE_TYPE: simple})@main.route(/,methods=[GET,POST])def index(): cache.clear() form = SearchForm() if form.validate_on_submit(): session[search_name] = form.name.data # 這裡的url_for(.search)實際是url_for(main.search) # 這裡省略了藍本名,但是跨藍本的重定向(redirect), # 必須使用帶有命名空間的端點名 return redirect(url_for(.search)) return render_template(index.html, form=form)@main.route(/search)def search(): search_name = session.get(search_name) data = book_number(search_name) return render_template(search.html,data=data,search_name=search_name)@main.route(/contents/<int:id>)def contents(id): search_name = session.get(search_name) bookname = book_number(search_name)[id] session[bookname] = bookname titles = get_allzj_title(search_name)[id] bookid = id data = contents_number(titles) return render_template(contents.html,data=data,bookid=bookid,bookname=bookname)@main.route(/post/<int:bookid>/<int:id>)@cache.cached(timeout=300, key_prefix=view_%s, unless=None)def post(bookid,id): search_name = session.get(search_name) title = get_allzj_title(search_name)[bookid][id] list = get_posts(search_name, bookid, id) post = list[0] return render_template(post.html, bookid=bookid, id=id, title=title,post=post,search_name=search_name)
啟動程序
頂級文件夾中的 manage.py 文件用於啟動程序。manage.py:
#!/usr/bin/env pythonimport osfrom app import create_appfrom flask_script import Manager, Shellapp = create_app(os.getenv(FLASK_CONFIG) or default)manager = Manager(app)if __name__ == __main__: manager.run()
這個腳本先創建程序,如果已經定義了環境變數 FLASK_CONFIG,則讀取配置名,否則使用默認。
需求文件
最後我們需要一個 requirements.txt 文件,記錄所有程序依賴包以及版本號。pip 可以使用下面的命令自動生成這個文件。
(venv) $ pip freeze >requirements.txt
如果你要創建這個虛擬環境的完全副本,可以創建新的虛擬環境,並且在上面運行如下命令:
(venv) $ pip install -r requirements.txt
這個時候我們的項目組織形式如下:
我們把爬蟲文件放在 spider 文件夾中。
上一篇文章在我室友的實踐下,出現很多問題,我也都更正了。感謝室友。
本來準備把後續的下載功能和用戶登錄功能更新出來的,但是由於篇幅太大,還是留到下一次有時間在更新吧,這次我們來實現一個文章翻頁功能。很簡單。
首先我們來寫模版文件。app/templates/post.html:
{% block head %}<!-- super() 可以在繼承模版上添加新內容。-->{{ super() }} <link rel="stylesheet" href="{{ url_for(static,filename=styles.css) }}" type="text/css">{% endblock %}{% block contents %}...<div class="page"> <ul> <li> <a href="/post/{{ bookid }}/{{ id - 1 }}">上一章</a> </li> <li> <a href="/contents/{{ bookid }}">目錄</a> </li> <li> <a href="/post/{{ bookid }}/{{ id + 1 }}">下一章</a> </li> </ul></div>...{% endblock %}
靜態文件也很簡單。app/static/styles.css:
.page li{ list-style: none; float:left; padding: 0 150px; line-height: 39px;}
現在運行程序,在文章頁面就可以看到我們的翻頁了。
好了這次的更新就這麼多了,大家下次再見。項目地址
github:Blackyukun/dingdian
謝謝大家閱讀,謝謝!
推薦閱讀:
※Python實現3D建模工具
※Flask模板引擎:Jinja2語法介紹
※OpenCV:圖片操作基本知識(二)
※數據分析入門必看案例:泰坦尼克號倖存率研究
※深入淺出區塊鏈(1)介紹