結構,如圖:python
Flask最上層是app核心對象
,在這個核心對象上能夠插入不少藍圖,這個藍圖是不能單獨存在的,必須將app做爲插板插入app ,在每個藍圖上,能夠註冊不少靜態文件,視圖函數,模板 ,一個業務模塊能夠作爲一個藍圖,好比book,以前的book.py 放到了app/web/路徑下,就是考慮到了藍圖,app屬因而整個Flask應用層,web屬因而藍圖mysql
一些初始化操做應該放入到__init__
文件中,好比Flask的核心應用app初始化對象,應該放入到在應用層級app包的 __init__.py
中 ,而藍圖的初始化應該放入到藍圖層的web包__init__.py
中,如圖:git
Flask的核心應用app初始化對象文件app/__init__.py
web
# -*- coding: utf-8 -*- from flask import Flask def create_app(): app = Flask(__name__) app.config.from_object('config') # 要返回回去 return app
# -*- coding: utf-8 -*- from app import create_app app = create_app() if __name__ == '__main__': app.run(debug=app.config['DEBUG'])
app/web/book.py
中,記得導入Blueprint# -*- coding: utf-8 -*- from flask import jsonify, Blueprint from helper import is_isbn_key from ShanqiuBook import ShanqiuBook # 藍圖 blueprint,進行初始化,藍圖的名字和參數爲藍圖所在的模塊名通常用__name__ web = Blueprint ('web',__name__) # 此時這裏用的就是web了 @web.route('/book/search/<q>/<page>') def hello(q,page): is_or_key = is_isbn_key(q) if is_or_key == 'isbn': result = ShanqiuBook.search_by_isbn(q) else: result = ShanqiuBook.search_by_keyword(q) return jsonify(result)
app/__init__.py
# -*- coding: utf-8 -*- from flask import Flask def create_app(): app = Flask(__name__) app.config.from_object('config') # 調用一下就能夠 register_blueprint(app) return app # 經過這個方法插入到app中 def register_blueprint(app): from app.web.book import web # 註冊這個藍圖對象 app.register_blueprint(web)
# -*- coding: utf-8 -*- from flask import jsonify, Blueprint from helper import is_isbn_key from ShanqiuBook import ShanqiuBook # 導入web模塊 from . import web @web.route('/book/search/<q>/<page>') def hello(q,page): # 調用方法判斷用戶是根據什麼查的 is_or_key = is_isbn_key(q) if is_or_key == 'isbn': result = ShanqiuBook.search_by_isbn(q) else: result = ShanqiuBook.search_by_keyword(q) return jsonify(result)
# -*- coding: utf-8 -*- # 導入web模塊 from . import web @web.route("/user/login") def login(): return "success"
app/web/__init__.py
文件中,定義這個藍圖對象# -*- coding: utf-8 -*- # 藍圖 blueprint,進行初始化 from flask import Blueprint web = Blueprint ('web',__name__) # 這兩個導入以後就能夠成功的運行對應模塊中相關的代碼,注意這個位置,這藍圖實例化以後 from app.web import book from app.web import user
/book/search/<q>/<page>
這種格式的,Flask會將<>裏的值自動映射成視圖函數方法的參數,可是這種格式用着不爽,要把用戶輸入的參數做爲請求參數傳入,這個時候就要使用這種格式了http://127.0.0.1:5000/book/search/?q=金庸&page=1
# -*- coding: utf-8 -*- # 導入這個request模塊, from flask import jsonify, Blueprint,request from helper import is_isbn_key from ShanqiuBook import ShanqiuBook from . import web # http://127.0.0.1:5000/book/search/?q=金庸&page=1 @web.route('/book/search/') def hello(): # 經過Request對象拿到對應值的信息,可是這個並非py中原始的字典,而是dict的子類immutableDict q = request.args['q'] page = request.args['page'] # ip = request.remote_addr # 經過這個方法把它轉換爲普通的dict # a = request.args.to_dict() # print(a) is_or_key = is_isbn_key(q) if is_or_key == 'isbn': result = ShanqiuBook.search_by_isbn(q) else: result = ShanqiuBook.search_by_keyword(q) return jsonify(result)
(flask--yQglGu4) E:\py\qiyue\flask>pipenv install wtforms
# -*- coding: utf-8 -*- # 導入須要使用的模塊 from wtforms import Form,StringField,IntegerField from wtforms.validators import Length,NumberRange class SearchForm(Form): # 直接調用內置對象 # 參數校驗規則: # 1.定義的屬性名q,page要與要校驗的參數同名 # 2.根據要傳入的參數類型選擇不一樣的Field類進行實例化 # 3.傳入一個數組,做爲校驗規則validators # 4.能夠設置默認值 q = StringField(validators=[DataRequired(),Length(min=1,max=30)]) page = IntegerField(validators=[NumberRange(min=1,max=10)],default=1)
# -*- coding: utf-8 -*- from flask import jsonify, Blueprint,request from helper import is_isbn_key from ShanqiuBook import ShanqiuBook from . import web # 導入參數校驗 from app.forms.book import SearchForm # http://127.0.0.1:5000/book/search/?q=金庸&page=1 @web.route('/book/search/') def hello(): # 驗證層 # 實例化咱們自定義的SearchForm,須要傳入一個字典做爲要校驗的參數 form = SearchForm(request.args) # validate()方法返回True/False來標示是否校驗經過 if form.validate(): # 從form中取出校驗後的q與page,而且清除空格 q = form.q.data.strip() page = form.page.data is_or_key = is_isbn_key(q) if is_or_key == 'isbn': result = ShanqiuBook.search_by_isbn(q) else: result = ShanqiuBook.search_by_keyword(q) return jsonify(result) else: return jsonify({'msg':'參數校驗失敗'})
@classmethod def search_by_key(cls, q, count=15, start=0): # count:每頁顯示的數量 # start:每頁的第一條數據的下標 url = cls.search_by_key_url.format(q, count, start) return HTTP.get(url)
這樣寫很是的不妥sql
在視圖函數中接收到的參數是page,代碼的封裝性,咱們應該把count和start的計算過程放到ShanqiuBook.py的 search_by_key方法中來寫數據庫
count的值爲了方便往後的管理,這個應該放入到配置文件中,以前的配置文件是config.py,在根目錄下,而這個應該放入到app目錄下,而關於一些比較隱私的配置信息要妥善處理,因此在app目錄下創建兩個文件,secure.py用來存放私密的配置信息,setting.py用於存放一些不重要的配置信息,以下json
app/secure.pyflask
# -*- coding: utf-8 -*- # 存放比較機密的配置文件,在上傳git的時候不該該上傳此文件 DEBUG = True
app/setting.pyapi
# -*- coding: utf-8 -*- # 生產環境和開發環境幾乎同樣的,不怎麼機密的配置文件 # 每頁顯示的數據量 PER_PAGE = 15
start的計算是一個單獨的邏輯,應該用封裝成一個方法,使用的時候直接調用數組
# 獲取每一頁的起始下標 @staticmethod def calculate_start(page): # 獲取配置信息中的每頁顯示的數量 return (page -1 ) * current_app.config['PER_PAGE']
重構後的ShanqiuBook.py
-*- coding: utf-8 -*- from httper import httper # 經過這種方式來導入當前的app對象,方便調用配置而文件 from flask import current_app class ShanqiuBook: isbn_url = 'http://t.yushu.im/v2/book/search/isbn/{}' keyword_url = 'http://t.yushu.im/v2/book/search?q={}&count={}&start={}' @classmethod def search_by_isbn(cls,isbn): url = cls.isbn_url.format(isbn) result = httper.get(url) return result @classmethod def search_by_keyword(cls,keyword,page=1): # 每頁顯示的數據(經過這種方式從配置文件中獲取到),每一頁的起始下標 url = cls.keyword_url.format(keyword,current_app.config['PER_PAGE'],cls.calculate_start(page)) result = httper.get(url) return result # 獲取每一頁的起始下標 @staticmethod def calculate_start(page): return (page -1 ) * current_app.config['PER_PAGE']
app/__init__.py
文件中把配置文件添加到app中#-- coding: utf-8 -- from flask import Flask def create_app(): app = Flask(name) # app.config.from_object('config') # 把配置文件裝載進來 app.config.from_object('app.secure') app.config.from_object('app.setting') register_blueprint(app) return app def register_blueprint(app): from app.web.book import web app.register_blueprint(web)
在app/models/book.py文件中創建模型,這裏使用到sqlalchemy來實現自動化映射,在Flask框架中對這個進行了改良Flask_SQLAlchemy,這個更加人性化,安裝(flask--yQglGu4) E:\py\qiyue\flask>pipenv install flask-sqlalchemy
創建模型,這樣就創建好了模型
# -*- coding: utf-8 -*- # 首先導入 from sqlalchemy import Column,Integer,String # sqlalchemy,自動化映射 # Flask_SQLAlchemy,這個是Flask封裝後的api,更加人性化 class Book(): # 須要把這些屬性的默認值寫成sqlalchemy提供的固定的類型 # Column()傳入參數:數據類型,主鍵,自增 id = Column(Integer,primary_key=True,autoincrement=True) # 數據類型,不爲空 title = Column(String(50),nullable=False) author = Column(String(30),default='未名') binding = Column(String(20)) publisher = Column(String(50)) price = Column(String(20)) pages = Column(Integer) pubdate = Column(String(20)) # 惟一:unique=True isbn = Column(String(15),nullable=False,unique=True) summary = Column(String(1000)) image = Column(String(50)) # 定義一些方法 def sample(self): pass
# -*- coding: utf-8 -*- from sqlalchemy import Column,Integer,String # 將模型映射到數據庫中 # 首先導入核心的對象 from flask_sqlalchemy import SQLAlchemy # 初始化 db = SQLAlchemy() # 繼承db.Model class Book(db.Model): id = Column(Integer,primary_key=True,autoincrement=True) title = Column(String(50),nullable=False) author = Column(String(30),default='未名') binding = Column(String(20)) publisher = Column(String(50)) price = Column(String(20)) pages = Column(Integer) pubdate = Column(String(20)) isbn = Column(String(15),nullable=False,unique=True) summary = Column(String(1000)) image = Column(String(50))
app/__init__.py
中進行模型與flask關聯# -*- coding: utf-8 -*- from flask import Flask # 導入這個db from app.models.book import db def create_app(): app = Flask(__name__) app.config.from_object('app.secure') app.config.from_object('app.setting') register_blueprint(app) # 把這個db和核心對象關聯起來了 db.init_app(app) # 注意這裏,這樣寫的話會報錯 db.create_all() # 把全部的數據模型映射到數據庫中 return app def register_blueprint(app): from app.web.book import web app.register_blueprint(web)
# -*- coding: utf-8 -*- # 存放比較機密的配置文件 DEBUG = True # 數據庫鏈接url,固定格式 # 要鏈接的數據庫類型,數據庫驅動(這裏還要進行安裝:pipenv install cymysql) SQLALCHEMY_DATABASE_URI = 'mysql+cymysql://root:123456@localhost:3306/book'
'No application found. Either work inside a view function or push'
這個是由於在Flask中,不是實例化了app核心對象,其餘代碼就能夠直接使用,要在上面的第二步的注意事項中` db.create_all()`方法中,把app核心對象傳入便可
db.create_all(app=app)
,這樣就能夠了,在數據庫中就能夠看到表了