Flask-藍圖、模型與CodeFirst

1、應用、藍圖與視圖函數

  1. 結構,如圖:python

    藍圖

  2. Flask最上層是app核心對象 ,在這個核心對象上能夠插入不少藍圖,這個藍圖是不能單獨存在的,必須將app做爲插板插入app ,在每個藍圖上,能夠註冊不少靜態文件,視圖函數,模板 ,一個業務模塊能夠作爲一個藍圖,好比book,以前的book.py 放到了app/web/路徑下,就是考慮到了藍圖,app屬因而整個Flask應用層,web屬因而藍圖mysql

  3. 一些初始化操做應該放入到__init__文件中,好比Flask的核心應用app初始化對象,應該放入到在應用層級app包的 __init__.py 中 ,而藍圖的初始化應該放入到藍圖層的web包__init__.py中,如圖:git

    結構圖

  4. Flask的核心應用app初始化對象文件app/__init__.pyweb

# -*- coding: utf-8 -*-
from flask import Flask

def create_app():
    app = Flask(__name__)
    app.config.from_object('config')
    # 要返回回去
    return app
  1. 此時在主文件中
# -*- coding: utf-8 -*-
from app import create_app

app = create_app()

if __name__ == '__main__':
    app.run(debug=app.config['DEBUG'])

2、用藍圖註冊視圖函數

  1. 在藍圖中註冊試圖函數,在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)
  1. 在藍圖中註冊了試圖函數,還須要把藍圖插入到app中,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)

3、單藍圖多模塊拆分視圖函數

  1. 藍圖,就是爲了分模塊的,好比一個web系統就是屬於一個web模塊,一個移動端使用的api就是一個api模塊,而咱們這裏的book,user等不一樣類別的py文件,要是每個都註冊一個藍圖的話就有點小題大做了,因此要進行單藍圖
  2. 在一個模塊(web)的初始文件中定義藍圖對象,而後這個模塊中的其餘的py文件引用的就是這一個藍圖對象來註冊路由函數,
  3. 在app/web/book.py文件中
# -*- 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)
  1. 這裏先創建一個僞代碼user.py,爲了多一個模塊進行演示
# -*- coding: utf-8 -*-
# 導入web模塊
from . import web
@web.route("/user/login")
def login():
    return "success"
  1. 此時在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

4、Request對象

  1. 在app/web/book.py文件中,定義的url請求是/book/search/<q>/<page>這種格式的,Flask會將<>裏的值自動映射成視圖函數方法的參數,可是這種格式用着不爽,要把用戶輸入的參數做爲請求參數傳入,這個時候就要使用這種格式了http://127.0.0.1:5000/book/search/?q=金庸&page=1
  2. 這個該怎麼獲取值呢,這個時候就用到Flask內置的Request了,經過request對象就能夠獲取HTTP請求中包含的詳細信息了,具體的用法看下面的代碼
# -*- 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)
  1. Flask的request是基於代理模式實現的,想讓request正常使用,必須確保是http請求觸發的函數或視圖函數中使用

5、WTForms參數驗證

  1. 上面咱們把url改了,可是若是用戶輸入了一些特殊的符號該怎麼辦?這個時候就要使用到參數驗證,而WTForms框架就是一個優秀的參數驗證框架,首先在對應的環境中進行安裝(flask--yQglGu4) E:\py\qiyue\flask>pipenv install wtforms
  2. 這個參數驗證寫在哪裏好呢,直接寫在book.py中,這樣是最不妥的,爲了方便調用,應該寫成一個類,因此寫在app/forms/book.py文件中
# -*- 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)
  1. 此時在app/web/book.py文件中就能夠直接調用就好了
# -*- 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':'參數校驗失敗'})

6、拆分配置文件

  1. 以前訪問數據的時候,count和start都是寫死的,如今來進行重構,以前的代碼
@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)
  1. 這樣寫很是的不妥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']
  2. 重構後的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']​
  1. 這個時候在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)

7、定義第一個模型類

  1. 咱們如今把文件進行整理,以下:

目錄結構

  1. 首先在本地建立一個數據庫,以下:

新建數據庫

  1. 在app/models/book.py文件中創建模型,這裏使用到sqlalchemy來實現自動化映射,在Flask框架中對這個進行了改良Flask_SQLAlchemy,這個更加人性化,安裝(flask--yQglGu4) E:\py\qiyue\flask>pipenv install flask-sqlalchemy

  2. 創建模型,這樣就創建好了模型

# -*- 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

8、將模型映射到數據庫中

  1. 在模型類app/models/book.py中引入導入核心對象,並實例化,繼承
# -*- 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))
  1. 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)
  1. 配置數據庫鏈接的配置文件在app/secure.py文件中
# -*- coding: utf-8 -*-

# 存放比較機密的配置文件
DEBUG = True

# 數據庫鏈接url,固定格式
# 要鏈接的數據庫類型,數據庫驅動(這裏還要進行安裝:pipenv install cymysql)
SQLALCHEMY_DATABASE_URI = 'mysql+cymysql://root:123456@localhost:3306/book'
  1. 以後運行項目,就會建立在指定的數據庫中建立一個數據表了,可是運行項目會出現下面的這種錯誤
'No application found. Either work inside a view function or push'
這個是由於在Flask中,不是實例化了app核心對象,其餘代碼就能夠直接使用,要在上面的第二步的注意事項中` db.create_all()`方法中,把app核心對象傳入便可

db.create_all(app=app),這樣就能夠了,在數據庫中就能夠看到表了

數據表結構

相關文章
相關標籤/搜索